@tiptap/extension-link 2.11.6 → 3.0.0-beta.0
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/LICENSE.md +21 -0
- package/README.md +5 -1
- package/dist/index.cjs +386 -398
- package/dist/index.cjs.map +1 -1
- package/dist/{link.d.ts → index.d.cts} +8 -7
- package/dist/index.d.ts +143 -4
- package/dist/index.js +359 -393
- package/dist/index.js.map +1 -1
- package/package.json +12 -10
- package/src/helpers/autolink.ts +10 -27
- package/src/helpers/clickHandler.ts +3 -3
- package/src/helpers/pasteHandler.ts +5 -3
- package/src/link.ts +85 -96
- package/dist/helpers/autolink.d.ts +0 -16
- package/dist/helpers/autolink.d.ts.map +0 -1
- package/dist/helpers/clickHandler.d.ts +0 -8
- package/dist/helpers/clickHandler.d.ts.map +0 -1
- package/dist/helpers/pasteHandler.d.ts +0 -11
- package/dist/helpers/pasteHandler.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.umd.js +0 -426
- package/dist/index.umd.js.map +0 -1
- package/dist/link.d.ts.map +0 -1
package/src/link.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from '@tiptap/
|
|
4
|
-
import { Plugin } from '@tiptap/pm/state'
|
|
1
|
+
import type { PasteRuleMatch } from '@tiptap/core'
|
|
2
|
+
import { Mark, markPasteRule, mergeAttributes } from '@tiptap/core'
|
|
3
|
+
import type { Plugin } from '@tiptap/pm/state'
|
|
5
4
|
import { find, registerCustomProtocol, reset } from 'linkifyjs'
|
|
6
5
|
|
|
7
6
|
import { autolink } from './helpers/autolink.js'
|
|
@@ -15,22 +14,23 @@ export interface LinkProtocolOptions {
|
|
|
15
14
|
* @example 'ftp'
|
|
16
15
|
* @example 'git'
|
|
17
16
|
*/
|
|
18
|
-
scheme: string
|
|
17
|
+
scheme: string
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* If enabled, it allows optional slashes after the protocol.
|
|
22
21
|
* @default false
|
|
23
22
|
* @example true
|
|
24
23
|
*/
|
|
25
|
-
optionalSlashes?: boolean
|
|
24
|
+
optionalSlashes?: boolean
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
export const pasteRegex =
|
|
27
|
+
export const pasteRegex =
|
|
28
|
+
/https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @deprecated The default behavior is now to open links when the editor is not editable.
|
|
32
32
|
*/
|
|
33
|
-
type DeprecatedOpenWhenNotEditable = 'whenNotEditable'
|
|
33
|
+
type DeprecatedOpenWhenNotEditable = 'whenNotEditable'
|
|
34
34
|
|
|
35
35
|
export interface LinkOptions {
|
|
36
36
|
/**
|
|
@@ -38,39 +38,39 @@ export interface LinkOptions {
|
|
|
38
38
|
* @default true
|
|
39
39
|
* @example false
|
|
40
40
|
*/
|
|
41
|
-
autolink: boolean
|
|
41
|
+
autolink: boolean
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* An array of custom protocols to be registered with linkifyjs.
|
|
45
45
|
* @default []
|
|
46
46
|
* @example ['ftp', 'git']
|
|
47
47
|
*/
|
|
48
|
-
protocols: Array<LinkProtocolOptions | string
|
|
48
|
+
protocols: Array<LinkProtocolOptions | string>
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Default protocol to use when no protocol is specified.
|
|
52
52
|
* @default 'http'
|
|
53
53
|
*/
|
|
54
|
-
defaultProtocol: string
|
|
54
|
+
defaultProtocol: string
|
|
55
55
|
/**
|
|
56
56
|
* If enabled, links will be opened on click.
|
|
57
57
|
* @default true
|
|
58
58
|
* @example false
|
|
59
59
|
*/
|
|
60
|
-
openOnClick: boolean | DeprecatedOpenWhenNotEditable
|
|
60
|
+
openOnClick: boolean | DeprecatedOpenWhenNotEditable
|
|
61
61
|
/**
|
|
62
62
|
* Adds a link to the current selection if the pasted content only contains an url.
|
|
63
63
|
* @default true
|
|
64
64
|
* @example false
|
|
65
65
|
*/
|
|
66
|
-
linkOnPaste: boolean
|
|
66
|
+
linkOnPaste: boolean
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* HTML attributes to add to the link element.
|
|
70
70
|
* @default {}
|
|
71
71
|
* @example { class: 'foo' }
|
|
72
72
|
*/
|
|
73
|
-
HTMLAttributes: Record<string, any
|
|
73
|
+
HTMLAttributes: Record<string, any>
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* @deprecated Use the `shouldAutoLink` option instead.
|
|
@@ -78,7 +78,7 @@ export interface LinkOptions {
|
|
|
78
78
|
* @param url - The url to be validated.
|
|
79
79
|
* @returns - True if the url is valid, false otherwise.
|
|
80
80
|
*/
|
|
81
|
-
validate: (url: string) => boolean
|
|
81
|
+
validate: (url: string) => boolean
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* A validation function which is used for configuring link verification for preventing XSS attacks.
|
|
@@ -100,17 +100,17 @@ export interface LinkOptions {
|
|
|
100
100
|
/**
|
|
101
101
|
* The default validation function.
|
|
102
102
|
*/
|
|
103
|
-
defaultValidate: (url: string) => boolean
|
|
103
|
+
defaultValidate: (url: string) => boolean
|
|
104
104
|
/**
|
|
105
105
|
* An array of allowed protocols for the URL (e.g., "http", "https"). As defined in the `protocols` option.
|
|
106
106
|
*/
|
|
107
|
-
protocols: Array<LinkProtocolOptions | string
|
|
107
|
+
protocols: Array<LinkProtocolOptions | string>
|
|
108
108
|
/**
|
|
109
109
|
* A string that represents the default protocol (e.g., 'http'). As defined in the `defaultProtocol` option.
|
|
110
110
|
*/
|
|
111
|
-
defaultProtocol: string
|
|
112
|
-
}
|
|
113
|
-
) => boolean
|
|
111
|
+
defaultProtocol: string
|
|
112
|
+
},
|
|
113
|
+
) => boolean
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* Determines whether a valid link should be automatically linked in the content.
|
|
@@ -118,7 +118,7 @@ export interface LinkOptions {
|
|
|
118
118
|
* @param {string} url - The URL that has already been validated.
|
|
119
119
|
* @returns {boolean} - True if the link should be auto-linked; false if it should not be auto-linked.
|
|
120
120
|
*/
|
|
121
|
-
shouldAutoLink: (url: string) => boolean
|
|
121
|
+
shouldAutoLink: (url: string) => boolean
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
declare module '@tiptap/core' {
|
|
@@ -130,28 +130,28 @@ declare module '@tiptap/core' {
|
|
|
130
130
|
* @example editor.commands.setLink({ href: 'https://tiptap.dev' })
|
|
131
131
|
*/
|
|
132
132
|
setLink: (attributes: {
|
|
133
|
-
href: string
|
|
134
|
-
target?: string | null
|
|
135
|
-
rel?: string | null
|
|
136
|
-
class?: string | null
|
|
137
|
-
}) => ReturnType
|
|
133
|
+
href: string
|
|
134
|
+
target?: string | null
|
|
135
|
+
rel?: string | null
|
|
136
|
+
class?: string | null
|
|
137
|
+
}) => ReturnType
|
|
138
138
|
/**
|
|
139
139
|
* Toggle a link mark
|
|
140
140
|
* @param attributes The link attributes
|
|
141
141
|
* @example editor.commands.toggleLink({ href: 'https://tiptap.dev' })
|
|
142
142
|
*/
|
|
143
143
|
toggleLink: (attributes: {
|
|
144
|
-
href: string
|
|
145
|
-
target?: string | null
|
|
146
|
-
rel?: string | null
|
|
147
|
-
class?: string | null
|
|
148
|
-
}) => ReturnType
|
|
144
|
+
href: string
|
|
145
|
+
target?: string | null
|
|
146
|
+
rel?: string | null
|
|
147
|
+
class?: string | null
|
|
148
|
+
}) => ReturnType
|
|
149
149
|
/**
|
|
150
150
|
* Unset a link mark
|
|
151
151
|
* @example editor.commands.unsetLink()
|
|
152
152
|
*/
|
|
153
|
-
unsetLink: () => ReturnType
|
|
154
|
-
}
|
|
153
|
+
unsetLink: () => ReturnType
|
|
154
|
+
}
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -161,18 +161,7 @@ declare module '@tiptap/core' {
|
|
|
161
161
|
const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
|
|
162
162
|
|
|
163
163
|
export function isAllowedUri(uri: string | undefined, protocols?: LinkOptions['protocols']) {
|
|
164
|
-
const allowedProtocols: string[] = [
|
|
165
|
-
'http',
|
|
166
|
-
'https',
|
|
167
|
-
'ftp',
|
|
168
|
-
'ftps',
|
|
169
|
-
'mailto',
|
|
170
|
-
'tel',
|
|
171
|
-
'callto',
|
|
172
|
-
'sms',
|
|
173
|
-
'cid',
|
|
174
|
-
'xmpp',
|
|
175
|
-
]
|
|
164
|
+
const allowedProtocols: string[] = ['http', 'https', 'ftp', 'ftps', 'mailto', 'tel', 'callto', 'sms', 'cid', 'xmpp']
|
|
176
165
|
|
|
177
166
|
if (protocols) {
|
|
178
167
|
protocols.forEach(protocol => {
|
|
@@ -185,16 +174,14 @@ export function isAllowedUri(uri: string | undefined, protocols?: LinkOptions['p
|
|
|
185
174
|
}
|
|
186
175
|
|
|
187
176
|
return (
|
|
188
|
-
!uri
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
),
|
|
197
|
-
)
|
|
177
|
+
!uri ||
|
|
178
|
+
uri.replace(ATTR_WHITESPACE, '').match(
|
|
179
|
+
new RegExp(
|
|
180
|
+
// eslint-disable-next-line no-useless-escape
|
|
181
|
+
`^(?:(?:${allowedProtocols.join('|')}):|[^a-z]|[a-z0-9+.\-]+(?:[^a-z+.\-:]|$))`,
|
|
182
|
+
'i',
|
|
183
|
+
),
|
|
184
|
+
)
|
|
198
185
|
)
|
|
199
186
|
}
|
|
200
187
|
|
|
@@ -215,9 +202,7 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
215
202
|
if (this.options.validate && !this.options.shouldAutoLink) {
|
|
216
203
|
// Copy the validate function to the shouldAutoLink option
|
|
217
204
|
this.options.shouldAutoLink = this.options.validate
|
|
218
|
-
console.warn(
|
|
219
|
-
'The `validate` option is deprecated. Rename to the `shouldAutoLink` option instead.',
|
|
220
|
-
)
|
|
205
|
+
console.warn('The `validate` option is deprecated. Rename to the `shouldAutoLink` option instead.')
|
|
221
206
|
}
|
|
222
207
|
this.options.protocols.forEach(protocol => {
|
|
223
208
|
if (typeof protocol === 'string') {
|
|
@@ -283,8 +268,8 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
283
268
|
|
|
284
269
|
// prevent XSS attacks
|
|
285
270
|
if (
|
|
286
|
-
!href
|
|
287
|
-
|
|
271
|
+
!href ||
|
|
272
|
+
!this.options.isAllowedUri(href, {
|
|
288
273
|
defaultValidate: url => !!isAllowedUri(url, this.options.protocols),
|
|
289
274
|
protocols: this.options.protocols,
|
|
290
275
|
defaultProtocol: this.options.defaultProtocol,
|
|
@@ -308,11 +293,7 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
308
293
|
})
|
|
309
294
|
) {
|
|
310
295
|
// strip out the href
|
|
311
|
-
return [
|
|
312
|
-
'a',
|
|
313
|
-
mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, href: '' }),
|
|
314
|
-
0,
|
|
315
|
-
]
|
|
296
|
+
return ['a', mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, href: '' }), 0]
|
|
316
297
|
}
|
|
317
298
|
|
|
318
299
|
return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
|
|
@@ -321,14 +302,17 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
321
302
|
addCommands() {
|
|
322
303
|
return {
|
|
323
304
|
setLink:
|
|
324
|
-
attributes =>
|
|
305
|
+
attributes =>
|
|
306
|
+
({ chain }) => {
|
|
325
307
|
const { href } = attributes
|
|
326
308
|
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
309
|
+
if (
|
|
310
|
+
!this.options.isAllowedUri(href, {
|
|
311
|
+
defaultValidate: url => !!isAllowedUri(url, this.options.protocols),
|
|
312
|
+
protocols: this.options.protocols,
|
|
313
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
314
|
+
})
|
|
315
|
+
) {
|
|
332
316
|
return false
|
|
333
317
|
}
|
|
334
318
|
|
|
@@ -336,14 +320,17 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
336
320
|
},
|
|
337
321
|
|
|
338
322
|
toggleLink:
|
|
339
|
-
attributes =>
|
|
323
|
+
attributes =>
|
|
324
|
+
({ chain }) => {
|
|
340
325
|
const { href } = attributes
|
|
341
326
|
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
327
|
+
if (
|
|
328
|
+
!this.options.isAllowedUri(href, {
|
|
329
|
+
defaultValidate: url => !!isAllowedUri(url, this.options.protocols),
|
|
330
|
+
protocols: this.options.protocols,
|
|
331
|
+
defaultProtocol: this.options.defaultProtocol,
|
|
332
|
+
})
|
|
333
|
+
) {
|
|
347
334
|
return false
|
|
348
335
|
}
|
|
349
336
|
|
|
@@ -354,11 +341,9 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
354
341
|
},
|
|
355
342
|
|
|
356
343
|
unsetLink:
|
|
357
|
-
() =>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.setMeta('preventAutolink', true)
|
|
361
|
-
.run()
|
|
344
|
+
() =>
|
|
345
|
+
({ chain }) => {
|
|
346
|
+
return chain().unsetMark(this.name, { extendEmptyMarkRange: true }).setMeta('preventAutolink', true).run()
|
|
362
347
|
},
|
|
363
348
|
}
|
|
364
349
|
},
|
|
@@ -372,8 +357,9 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
372
357
|
if (text) {
|
|
373
358
|
const { protocols, defaultProtocol } = this.options
|
|
374
359
|
const links = find(text).filter(
|
|
375
|
-
item =>
|
|
376
|
-
|
|
360
|
+
item =>
|
|
361
|
+
item.isLink &&
|
|
362
|
+
this.options.isAllowedUri(item.value, {
|
|
377
363
|
defaultValidate: href => !!isAllowedUri(href, protocols),
|
|
378
364
|
protocols,
|
|
379
365
|
defaultProtocol,
|
|
@@ -381,13 +367,15 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
381
367
|
)
|
|
382
368
|
|
|
383
369
|
if (links.length) {
|
|
384
|
-
links.forEach(link =>
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
370
|
+
links.forEach(link =>
|
|
371
|
+
foundLinks.push({
|
|
372
|
+
text: link.value,
|
|
373
|
+
data: {
|
|
374
|
+
href: link.href,
|
|
375
|
+
},
|
|
376
|
+
index: link.start,
|
|
377
|
+
}),
|
|
378
|
+
)
|
|
391
379
|
}
|
|
392
380
|
}
|
|
393
381
|
|
|
@@ -412,11 +400,12 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
412
400
|
autolink({
|
|
413
401
|
type: this.type,
|
|
414
402
|
defaultProtocol: this.options.defaultProtocol,
|
|
415
|
-
validate: url =>
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
403
|
+
validate: url =>
|
|
404
|
+
this.options.isAllowedUri(url, {
|
|
405
|
+
defaultValidate: href => !!isAllowedUri(href, protocols),
|
|
406
|
+
protocols,
|
|
407
|
+
defaultProtocol,
|
|
408
|
+
}),
|
|
420
409
|
shouldAutoLink: this.options.shouldAutoLink,
|
|
421
410
|
}),
|
|
422
411
|
)
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { MarkType } from '@tiptap/pm/model';
|
|
2
|
-
import { Plugin } from '@tiptap/pm/state';
|
|
3
|
-
type AutolinkOptions = {
|
|
4
|
-
type: MarkType;
|
|
5
|
-
defaultProtocol: string;
|
|
6
|
-
validate: (url: string) => boolean;
|
|
7
|
-
shouldAutoLink: (url: string) => boolean;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* This plugin allows you to automatically add links to your editor.
|
|
11
|
-
* @param options The plugin options
|
|
12
|
-
* @returns The plugin instance
|
|
13
|
-
*/
|
|
14
|
-
export declare function autolink(options: AutolinkOptions): Plugin;
|
|
15
|
-
export {};
|
|
16
|
-
//# sourceMappingURL=autolink.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"autolink.d.ts","sourceRoot":"","sources":["../../src/helpers/autolink.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAA;AAyBpD,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,QAAQ,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;IAClC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CACzC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAgIzD"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { MarkType } from '@tiptap/pm/model';
|
|
2
|
-
import { Plugin } from '@tiptap/pm/state';
|
|
3
|
-
type ClickHandlerOptions = {
|
|
4
|
-
type: MarkType;
|
|
5
|
-
};
|
|
6
|
-
export declare function clickHandler(options: ClickHandlerOptions): Plugin;
|
|
7
|
-
export {};
|
|
8
|
-
//# sourceMappingURL=clickHandler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clickHandler.d.ts","sourceRoot":"","sources":["../../src/helpers/clickHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAA;AAEpD,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAyCjE"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Editor } from '@tiptap/core';
|
|
2
|
-
import { MarkType } from '@tiptap/pm/model';
|
|
3
|
-
import { Plugin } from '@tiptap/pm/state';
|
|
4
|
-
type PasteHandlerOptions = {
|
|
5
|
-
editor: Editor;
|
|
6
|
-
defaultProtocol: string;
|
|
7
|
-
type: MarkType;
|
|
8
|
-
};
|
|
9
|
-
export declare function pasteHandler(options: PasteHandlerOptions): Plugin;
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=pasteHandler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pasteHandler.d.ts","sourceRoot":"","sources":["../../src/helpers/pasteHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAA;AAGpD,KAAK,mBAAmB,GAAG;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CA+BjE"}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,cAAc,WAAW,CAAA;AAEzB,eAAe,IAAI,CAAA"}
|