@portabletext/markdown 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,32 +0,0 @@
1
- import type {PortableTextListItemRenderer} from '../types'
2
-
3
- /**
4
- * @public
5
- */
6
- export const DefaultListItemRenderer: PortableTextListItemRenderer = ({
7
- children,
8
- value,
9
- listIndex,
10
- }) => {
11
- const listStyle = value.listItem || 'bullet'
12
- const level = value.level || 1
13
-
14
- if (listStyle === 'number') {
15
- const indent = ' '.repeat(level - 1)
16
-
17
- return `${indent}${listIndex ?? 1}. ${children}`
18
- }
19
-
20
- const indent = ' '.repeat(level - 1)
21
-
22
- return `${indent}- ${children}`
23
- }
24
-
25
- /**
26
- * @public
27
- */
28
- export const DefaultUnknownListItemRenderer: PortableTextListItemRenderer = ({
29
- children,
30
- }) => {
31
- return `- ${children}\n`
32
- }
@@ -1,113 +0,0 @@
1
- import type {TypedObject} from '@portabletext/types'
2
- import type {PortableTextMarkRenderer} from '../types'
3
-
4
- /**
5
- * @public
6
- */
7
- export const DefaultEmRenderer: PortableTextMarkRenderer = ({children}) =>
8
- `_${children}_`
9
-
10
- /**
11
- * @public
12
- */
13
- export const DefaultStrongRenderer: PortableTextMarkRenderer = ({children}) =>
14
- `**${children}**`
15
-
16
- /**
17
- * @public
18
- */
19
- export const DefaultCodeRenderer: PortableTextMarkRenderer = ({children}) =>
20
- `\`${children}\``
21
-
22
- /**
23
- * @public
24
- */
25
- export const DefaultUnderlineRenderer: PortableTextMarkRenderer = ({
26
- children,
27
- }) => `<u>${children}</u>`
28
-
29
- /**
30
- * @public
31
- */
32
- export const DefaultStrikeThroughRenderer: PortableTextMarkRenderer = ({
33
- children,
34
- }) => `~~${children}~~`
35
-
36
- interface DefaultLink extends TypedObject {
37
- _type: 'link'
38
- href: string
39
- title: string | undefined
40
- }
41
-
42
- /**
43
- * @public
44
- */
45
- export const DefaultLinkRenderer: PortableTextMarkRenderer<DefaultLink> = ({
46
- children,
47
- value,
48
- }) => {
49
- const href = value?.href || ''
50
- const title = value?.title || ''
51
- const looksSafe = uriLooksSafe(href)
52
-
53
- if (looksSafe) {
54
- // Check if the URL looks like an HTML injection attempt
55
- // If it has quotes AND angle brackets, or other suspicious patterns, encode more aggressively
56
- const looksLikeInjection = /["'][^"']*[<>]|[<>][^<>]*["']/.test(href)
57
-
58
- if (looksLikeInjection) {
59
- // Encode all special characters that could be used for injection
60
- const encodedHref = href.replace(/["<>() ]/g, (char) => {
61
- return `%${char.charCodeAt(0).toString(16).toUpperCase()}`
62
- })
63
- return `[${children}](${encodedHref})`
64
- }
65
-
66
- // For normal URLs, don't encode parentheses - Markdown handles balanced parens fine
67
- return `[${children}](${href}${title ? ` "${title}"` : ''})`
68
- }
69
-
70
- // Return children without link when URL is unsafe
71
- return children
72
- }
73
-
74
- function uriLooksSafe(uri: string): boolean {
75
- const url = (uri || '').trim()
76
- const first = url.charAt(0)
77
-
78
- if (first === '#' || first === '/') {
79
- return true
80
- }
81
-
82
- const colonIndex = url.indexOf(':')
83
- if (colonIndex === -1) {
84
- return true
85
- }
86
-
87
- const allowedProtocols = ['http', 'https', 'mailto', 'tel']
88
- const proto = url.slice(0, colonIndex).toLowerCase()
89
- if (allowedProtocols.indexOf(proto) !== -1) {
90
- return true
91
- }
92
-
93
- const queryIndex = url.indexOf('?')
94
- if (queryIndex !== -1 && colonIndex > queryIndex) {
95
- return true
96
- }
97
-
98
- const hashIndex = url.indexOf('#')
99
- if (hashIndex !== -1 && colonIndex > hashIndex) {
100
- return true
101
- }
102
-
103
- return false
104
- }
105
-
106
- /**
107
- * @public
108
- */
109
- export const DefaultUnknownMarkRenderer: PortableTextMarkRenderer = ({
110
- children,
111
- }) => {
112
- return children
113
- }
@@ -1,79 +0,0 @@
1
- import type {PortableTextBlock} from '@portabletext/types'
2
- import type {PortableTextRenderer} from '../types'
3
-
4
- type PortableTextBlockRenderer = PortableTextRenderer<PortableTextBlock>
5
-
6
- /**
7
- * @public
8
- */
9
- export const DefaultNormalRenderer: PortableTextBlockRenderer = ({
10
- children,
11
- }) => {
12
- // Empty blocks should not add extra spacing
13
- if (!children || children.trim() === '') {
14
- return ''
15
- }
16
-
17
- return children
18
- }
19
-
20
- /**
21
- * @public
22
- */
23
- export const DefaultBlockquoteRenderer: PortableTextBlockRenderer = ({
24
- children,
25
- }) => {
26
- // Prefix each line with "> " for proper blockquote formatting
27
- // This handles multi-line content and preserves empty lines
28
- if (!children) return '>'
29
-
30
- return children
31
- .split('\n')
32
- .map((line) => `> ${line}`)
33
- .join('\n')
34
- }
35
-
36
- /**
37
- * @public
38
- */
39
- export const DefaultH1Renderer: PortableTextBlockRenderer = ({children}) =>
40
- `# ${children}`
41
-
42
- /**
43
- * @public
44
- */
45
- export const DefaultH2Renderer: PortableTextBlockRenderer = ({children}) =>
46
- `## ${children}`
47
-
48
- /**
49
- * @public
50
- */
51
- export const DefaultH3Renderer: PortableTextBlockRenderer = ({children}) =>
52
- `### ${children}`
53
-
54
- /**
55
- * @public
56
- */
57
- export const DefaultH4Renderer: PortableTextBlockRenderer = ({children}) =>
58
- `#### ${children}`
59
-
60
- /**
61
- * @public
62
- */
63
- export const DefaultH5Renderer: PortableTextBlockRenderer = ({children}) =>
64
- `##### ${children}`
65
-
66
- /**
67
- * @public
68
- */
69
- export const DefaultH6Renderer: PortableTextBlockRenderer = ({children}) =>
70
- `###### ${children}`
71
-
72
- /**
73
- * @public
74
- */
75
- export const DefaultUnknownStyleRenderer: PortableTextBlockRenderer = ({
76
- children,
77
- }) => {
78
- return children ?? ''
79
- }
@@ -1,126 +0,0 @@
1
- import type {PortableTextBlock} from '@portabletext/types'
2
- import type {PortableTextTypeRenderer} from '../types'
3
-
4
- /**
5
- * @public
6
- */
7
- export const DefaultCodeBlockRenderer: PortableTextTypeRenderer<{
8
- _type: 'code'
9
- code: string
10
- language: string | undefined
11
- }> = ({value}) => {
12
- return `\`\`\`${value.language ?? ''}\n${value.code}\n\`\`\``
13
- }
14
-
15
- /**
16
- * @public
17
- */
18
- export const DefaultHorizontalRuleRenderer: PortableTextTypeRenderer = () => {
19
- return '---'
20
- }
21
-
22
- /**
23
- * @public
24
- */
25
- export const DefaultHtmlRenderer: PortableTextTypeRenderer<{
26
- _type: 'html'
27
- html: string
28
- }> = ({value}) => {
29
- return value.html
30
- }
31
-
32
- /**
33
- * @public
34
- */
35
- export const DefaultImageRenderer: PortableTextTypeRenderer<{
36
- _type: 'image'
37
- src: string
38
- alt: string | undefined
39
- title: string | undefined
40
- }> = ({value}) => {
41
- const alt = value.alt ?? ''
42
- const title = value.title ? ` "${value.title}"` : ''
43
- return `![${alt}](${value.src}${title})`
44
- }
45
-
46
- /**
47
- * @public
48
- */
49
- export const DefaultTableRenderer: PortableTextTypeRenderer<{
50
- _type: 'table'
51
- headerRows: number | undefined
52
- rows: Array<{
53
- _key: string
54
- cells: Array<{
55
- _key: string
56
- value: Array<PortableTextBlock>
57
- }>
58
- }>
59
- }> = ({value, renderNode}) => {
60
- const headerRows = value.headerRows || 0
61
- const rows = value.rows as Array<{
62
- _key: string
63
- _type: 'row'
64
- cells: Array<{
65
- _type: 'cell'
66
- _key: string
67
- value: Array<{_type: string; children?: Array<unknown>}>
68
- }>
69
- }>
70
- const lines: string[] = []
71
-
72
- // Helper to extract text from cell blocks
73
- const getCellText = (
74
- cellBlocks: Array<{_type: string; children?: Array<unknown>}>,
75
- ): string => {
76
- return cellBlocks
77
- .map((block, index) =>
78
- renderNode({
79
- node: block as {_type: string},
80
- index,
81
- isInline: false,
82
- renderNode,
83
- }),
84
- )
85
- .join(' ')
86
- .trim()
87
- }
88
-
89
- // Add header rows
90
- for (let i = 0; i < headerRows; i++) {
91
- const row = rows[i]
92
- if (row) {
93
- const cellTexts = row.cells.map((cell) => getCellText(cell.value))
94
- lines.push(`| ${cellTexts.join(' | ')} |`)
95
- }
96
- }
97
-
98
- // Add separator line if there are headers
99
- if (headerRows > 0 && rows[0]) {
100
- const separators = rows[0].cells.map(() => ' --- ')
101
- lines.push(`|${separators.join('|')}|`)
102
- }
103
-
104
- // Add body rows
105
- for (let i = headerRows; i < rows.length; i++) {
106
- const row = rows[i]
107
- if (row) {
108
- const cellTexts = row.cells.map((cell) => getCellText(cell.value))
109
- lines.push(`| ${cellTexts.join(' | ')} |`)
110
- }
111
- }
112
-
113
- return lines.join('\n')
114
- }
115
-
116
- /**
117
- * @public
118
- */
119
- export const DefaultUnknownTypeRenderer: PortableTextTypeRenderer = ({
120
- value,
121
- isInline,
122
- }) => {
123
- const json = `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``
124
- // For inline unknown types, add newlines to break them out of the text flow
125
- return isInline ? `\n${json}\n` : json
126
- }
@@ -1,240 +0,0 @@
1
- import type {
2
- ArbitraryTypedObject,
3
- PortableTextBlock,
4
- PortableTextBlockStyle,
5
- PortableTextListItemBlock,
6
- PortableTextListItemType,
7
- TypedObject,
8
- } from '@portabletext/types'
9
-
10
- /**
11
- * Generic type for portable text renderers that takes blocks/inline blocks
12
- *
13
- * @public
14
- */
15
- export type PortableTextRenderer<N> = (
16
- options: PortableTextRendererOptions<N>,
17
- ) => string
18
-
19
- /**
20
- * Renderer function type for rendering portable text blocks (paragraphs, headings, blockquotes etc)
21
- *
22
- * @public
23
- */
24
- export type PortableTextBlockRenderer = PortableTextRenderer<PortableTextBlock>
25
-
26
- /**
27
- * Renderer function type for rendering portable text list items
28
- *
29
- * @public
30
- */
31
- export type PortableTextListItemRenderer =
32
- PortableTextRenderer<PortableTextListItemBlock>
33
-
34
- /**
35
- * Renderer function type for rendering portable text marks and/or decorators
36
- *
37
- * @public
38
- */
39
- export type PortableTextMarkRenderer<M extends TypedObject = any> = (
40
- options: PortableTextMarkRendererOptions<M>,
41
- ) => string
42
-
43
- /**
44
- * @public
45
- */
46
- export type PortableTextTypeRenderer<V extends TypedObject = any> = (
47
- options: PortableTextTypeRendererOptions<V>,
48
- ) => string
49
-
50
- /**
51
- * Object defining the different renderer functions to use for rendering various aspects
52
- * of Portable Text and user-provided types to Markdown.
53
- *
54
- * @public
55
- */
56
- export interface PortableTextRenderers {
57
- /**
58
- * Object of renderer functions for different types of objects that might appear
59
- * both as part of the blocks array, or as inline objects _inside_ of a block,
60
- * alongside text spans.
61
- *
62
- * Use the `isInline` property to check whether or not this is an inline object or a block.
63
- *
64
- * The object has the shape `{typeName: RendererFn}`, where `typeName` is the value set
65
- * in individual `_type` attributes.
66
- */
67
- types: Record<string, PortableTextTypeRenderer | undefined>
68
-
69
- /**
70
- * Object of renderer functions for different types of marks that might appear in spans.
71
- *
72
- * The object has the shape `{markName: RendererFn}`, where `markName` is the value set
73
- * in individual `_type` attributes, values being stored in the parent blocks `markDefs`.
74
- */
75
- marks: Record<string, PortableTextMarkRenderer | undefined>
76
-
77
- /**
78
- * Object of renderer functions for blocks with different `style` properties.
79
- *
80
- * The object has the shape `{styleName: RendererFn}`, where `styleName` is the value set
81
- * in individual `style` attributes on blocks.
82
- *
83
- * Can also be set to a single renderer function, which would handle block styles of _any_ type.
84
- */
85
- block:
86
- | Record<PortableTextBlockStyle, PortableTextBlockRenderer | undefined>
87
- | PortableTextBlockRenderer
88
-
89
- /**
90
- * Object of renderer functions used to render different list item styles.
91
- *
92
- * The object has the shape `{listItemType: RendererFn}`, where `listItemType` is the value
93
- * set in individual `listItem` attributes on blocks.
94
- *
95
- * Can also be set to a single renderer function, which would handle list items of _any_ type.
96
- */
97
- listItem:
98
- | Record<PortableTextListItemType, PortableTextListItemRenderer | undefined>
99
- | PortableTextListItemRenderer
100
-
101
- /**
102
- * Renderer for "hard breaks", eg `\n` inside of text spans.
103
- * By default renders as Markdown hard break (` \n` - two trailing spaces).
104
- */
105
- hardBreak: () => string
106
-
107
- /**
108
- * Renderer function used when encountering a mark type there is no registered renderer for
109
- * in the `marks` option.
110
- */
111
- unknownMark: PortableTextMarkRenderer
112
-
113
- /**
114
- * Renderer function used when encountering an object type there is no registered renderer for
115
- * in the `types` option.
116
- */
117
- unknownType: PortableTextRenderer<UnknownNodeType>
118
-
119
- /**
120
- * Renderer function used when encountering a block style there is no registered renderer for
121
- * in the `block` option. Only used if `block` is an object.
122
- */
123
- unknownBlockStyle: PortableTextRenderer<PortableTextBlock>
124
-
125
- /**
126
- * Renderer function used when encountering a list item style there is no registered renderer for
127
- * in the `listItem` option. Only used if `listItem` is an object.
128
- */
129
- unknownListItem: PortableTextRenderer<PortableTextListItemBlock>
130
- }
131
-
132
- /**
133
- * Options received by most Portable Text renderers
134
- *
135
- * @public
136
- */
137
- export interface PortableTextRendererOptions<T> {
138
- /**
139
- * Data associated with this portable text node, eg the raw JSON value of a block/type
140
- */
141
- value: T
142
-
143
- /**
144
- * Index within its parent
145
- */
146
- index: number
147
-
148
- /**
149
- * Index of a list item
150
- */
151
- listIndex?: number | undefined
152
-
153
- /**
154
- * Whether or not this node is "inline" - ie as a child of a text block,
155
- * alongside text spans, or a block in and of itself.
156
- */
157
- isInline: boolean
158
-
159
- /**
160
- * Serialized Markdown of child nodes of this block/type
161
- */
162
- children?: string
163
-
164
- /**
165
- * Function used to render any node that might appear in a portable text array or block,
166
- * including virtual "toolkit"-nodes like lists and nested spans. You will rarely need
167
- * to use this.
168
- */
169
- renderNode: RenderNode
170
- }
171
-
172
- /**
173
- * Options received by any user-defined type renderer in the input array that is not a text block
174
- *
175
- * @public
176
- */
177
- export type PortableTextTypeRendererOptions<T> = Omit<
178
- PortableTextRendererOptions<T>,
179
- 'children'
180
- >
181
-
182
- /**
183
- * Options received by Portable Text mark renderers
184
- *
185
- * @public
186
- */
187
- export interface PortableTextMarkRendererOptions<
188
- M extends TypedObject = ArbitraryTypedObject,
189
- > {
190
- /**
191
- * Mark definition, eg the actual data of the annotation. If the mark is a simple decorator, this will be `undefined`
192
- */
193
- value?: M
194
-
195
- /**
196
- * Text content of this mark
197
- */
198
- text: string
199
-
200
- /**
201
- * Key for this mark. The same key can be used amongst multiple text spans within the same block, so don't rely on this to be unique.
202
- */
203
- markKey: string | undefined
204
-
205
- /**
206
- * Type of mark - ie value of `_type` in the case of annotations, or the name of the decorator otherwise - eg `em`, `italic`.
207
- */
208
- markType: string
209
-
210
- /**
211
- * Serialized Markdown of child nodes of this mark
212
- */
213
- children: string
214
-
215
- /**
216
- * Function used to render any node that might appear in a portable text array or block,
217
- * including virtual "toolkit"-nodes like lists and nested spans. You will rarely need
218
- * to use this.
219
- */
220
- renderNode: RenderNode
221
- }
222
-
223
- /**
224
- * Any node type that we can't identify - eg it has an `_type`,
225
- * but we don't know anything about its other properties
226
- */
227
- export type UnknownNodeType =
228
- | {[key: string]: unknown; _type: string}
229
- | TypedObject
230
-
231
- export type RenderNode = <T extends TypedObject>(
232
- options: Serializable<T>,
233
- ) => string
234
-
235
- export interface Serializable<T> {
236
- node: T
237
- index: number
238
- isInline: boolean
239
- renderNode: RenderNode
240
- }
package/src/index.ts DELETED
@@ -1,51 +0,0 @@
1
- export {portableTextToMarkdown} from './from-portable-text/portable-text-to-markdown'
2
- export {
3
- DefaultBlockSpacingRenderer,
4
- type BlockSpacingRenderer,
5
- } from './from-portable-text/renderers/block-spacing'
6
- export {DefaultHardBreakRenderer} from './from-portable-text/renderers/hard-break'
7
- export {DefaultListItemRenderer} from './from-portable-text/renderers/list-item'
8
- export {
9
- DefaultBlockquoteRenderer,
10
- DefaultH1Renderer,
11
- DefaultH2Renderer,
12
- DefaultH3Renderer,
13
- DefaultH4Renderer,
14
- DefaultH5Renderer,
15
- DefaultH6Renderer,
16
- DefaultNormalRenderer,
17
- } from './from-portable-text/renderers/style'
18
- export {
19
- DefaultCodeRenderer,
20
- DefaultEmRenderer,
21
- DefaultLinkRenderer,
22
- DefaultStrikeThroughRenderer,
23
- DefaultStrongRenderer,
24
- DefaultUnderlineRenderer,
25
- } from './from-portable-text/renderers/marks'
26
- export {
27
- DefaultCodeBlockRenderer,
28
- DefaultHorizontalRuleRenderer,
29
- DefaultHtmlRenderer,
30
- DefaultImageRenderer,
31
- DefaultTableRenderer,
32
- } from './from-portable-text/renderers/type'
33
- export type {
34
- PortableTextBlockRenderer,
35
- PortableTextListItemRenderer,
36
- PortableTextMarkRenderer,
37
- PortableTextMarkRendererOptions,
38
- PortableTextRenderer,
39
- PortableTextRendererOptions,
40
- PortableTextRenderers,
41
- PortableTextTypeRenderer,
42
- PortableTextTypeRendererOptions,
43
- } from './from-portable-text/types'
44
- export {markdownToPortableText} from './to-portable-text/markdown-to-portable-text'
45
- export type {
46
- AnnotationMatcher,
47
- DecoratorMatcher,
48
- ListItemMatcher,
49
- ObjectMatcher,
50
- StyleMatcher,
51
- } from './to-portable-text/matchers'
@@ -1,32 +0,0 @@
1
- export function defaultKeyGenerator() {
2
- return randomKey(12)
3
- }
4
-
5
- const getByteHexTable = (() => {
6
- let table: any[]
7
- return () => {
8
- if (table) {
9
- return table
10
- }
11
-
12
- table = []
13
- for (let i = 0; i < 256; ++i) {
14
- table[i] = (i + 0x100).toString(16).slice(1)
15
- }
16
- return table
17
- }
18
- })()
19
-
20
- // WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html
21
- function whatwgRNG(length = 16) {
22
- const rnds8 = new Uint8Array(length)
23
- crypto.getRandomValues(rnds8)
24
- return rnds8
25
- }
26
-
27
- function randomKey(length?: number): string {
28
- const table = getByteHexTable()
29
- return whatwgRNG(length)
30
- .reduce((str, n) => str + table[n], '')
31
- .slice(0, length)
32
- }