@tiptap/extension-link
Advanced tools
Comparing version 3.0.0-next.0 to 3.0.0-next.1
@@ -1,339 +0,342 @@ | ||
import { combineTransactionSteps, getChangedRanges, findChildrenInRange, getMarksBetween, getAttributes, Mark, mergeAttributes, markPasteRule } from '@tiptap/core'; | ||
import { tokenize, find, registerCustomProtocol, reset } from 'linkifyjs'; | ||
import { Plugin, PluginKey } from '@tiptap/pm/state'; | ||
// src/link.ts | ||
import { | ||
Mark, | ||
markPasteRule, | ||
mergeAttributes | ||
} from "@tiptap/core"; | ||
import { find as find2, registerCustomProtocol, reset } from "linkifyjs"; | ||
/** | ||
* Check if the provided tokens form a valid link structure, which can either be a single link token | ||
* or a link token surrounded by parentheses or square brackets. | ||
* | ||
* This ensures that only complete and valid text is hyperlinked, preventing cases where a valid | ||
* top-level domain (TLD) is immediately followed by an invalid character, like a number. For | ||
* example, with the `find` method from Linkify, entering `example.com1` would result in | ||
* `example.com` being linked and the trailing `1` left as plain text. By using the `tokenize` | ||
* method, we can perform more comprehensive validation on the input text. | ||
*/ | ||
// src/helpers/autolink.ts | ||
import { | ||
combineTransactionSteps, | ||
findChildrenInRange, | ||
getChangedRanges, | ||
getMarksBetween | ||
} from "@tiptap/core"; | ||
import { Plugin, PluginKey } from "@tiptap/pm/state"; | ||
import { tokenize } from "linkifyjs"; | ||
function isValidLinkStructure(tokens) { | ||
if (tokens.length === 1) { | ||
return tokens[0].isLink; | ||
} | ||
if (tokens.length === 3 && tokens[1].isLink) { | ||
return ['()', '[]'].includes(tokens[0].value + tokens[2].value); | ||
} | ||
return false; | ||
if (tokens.length === 1) { | ||
return tokens[0].isLink; | ||
} | ||
if (tokens.length === 3 && tokens[1].isLink) { | ||
return ["()", "[]"].includes(tokens[0].value + tokens[2].value); | ||
} | ||
return false; | ||
} | ||
/** | ||
* This plugin allows you to automatically add links to your editor. | ||
* @param options The plugin options | ||
* @returns The plugin instance | ||
*/ | ||
function autolink(options) { | ||
return new Plugin({ | ||
key: new PluginKey('autolink'), | ||
appendTransaction: (transactions, oldState, newState) => { | ||
/** | ||
* Does the transaction change the document? | ||
*/ | ||
const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc); | ||
/** | ||
* Prevent autolink if the transaction is not a document change or if the transaction has the meta `preventAutolink`. | ||
*/ | ||
const preventAutolink = transactions.some(transaction => transaction.getMeta('preventAutolink')); | ||
/** | ||
* Prevent autolink if the transaction is not a document change | ||
* or if the transaction has the meta `preventAutolink`. | ||
*/ | ||
if (!docChanges || preventAutolink) { | ||
return; | ||
return new Plugin({ | ||
key: new PluginKey("autolink"), | ||
appendTransaction: (transactions, oldState, newState) => { | ||
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc); | ||
const preventAutolink = transactions.some((transaction) => transaction.getMeta("preventAutolink")); | ||
if (!docChanges || preventAutolink) { | ||
return; | ||
} | ||
const { tr } = newState; | ||
const transform = combineTransactionSteps(oldState.doc, [...transactions]); | ||
const changes = getChangedRanges(transform); | ||
changes.forEach(({ newRange }) => { | ||
const nodesInChangedRanges = findChildrenInRange( | ||
newState.doc, | ||
newRange, | ||
(node) => node.isTextblock | ||
); | ||
let textBlock; | ||
let textBeforeWhitespace; | ||
if (nodesInChangedRanges.length > 1) { | ||
textBlock = nodesInChangedRanges[0]; | ||
textBeforeWhitespace = newState.doc.textBetween( | ||
textBlock.pos, | ||
textBlock.pos + textBlock.node.nodeSize, | ||
void 0, | ||
" " | ||
); | ||
} else if (nodesInChangedRanges.length && newState.doc.textBetween(newRange.from, newRange.to, " ", " ").endsWith(" ")) { | ||
textBlock = nodesInChangedRanges[0]; | ||
textBeforeWhitespace = newState.doc.textBetween( | ||
textBlock.pos, | ||
newRange.to, | ||
void 0, | ||
" " | ||
); | ||
} | ||
if (textBlock && textBeforeWhitespace) { | ||
const wordsBeforeWhitespace = textBeforeWhitespace.split(" ").filter((s) => s !== ""); | ||
if (wordsBeforeWhitespace.length <= 0) { | ||
return false; | ||
} | ||
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1]; | ||
const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace); | ||
if (!lastWordBeforeSpace) { | ||
return false; | ||
} | ||
const linksBeforeSpace = tokenize(lastWordBeforeSpace).map((t) => t.toObject(options.defaultProtocol)); | ||
if (!isValidLinkStructure(linksBeforeSpace)) { | ||
return false; | ||
} | ||
linksBeforeSpace.filter((link) => link.isLink).map((link) => ({ | ||
...link, | ||
from: lastWordAndBlockOffset + link.start + 1, | ||
to: lastWordAndBlockOffset + link.end + 1 | ||
})).filter((link) => { | ||
if (!newState.schema.marks.code) { | ||
return true; | ||
} | ||
const { tr } = newState; | ||
const transform = combineTransactionSteps(oldState.doc, [...transactions]); | ||
const changes = getChangedRanges(transform); | ||
changes.forEach(({ newRange }) => { | ||
// Now let’s see if we can add new links. | ||
const nodesInChangedRanges = findChildrenInRange(newState.doc, newRange, node => node.isTextblock); | ||
let textBlock; | ||
let textBeforeWhitespace; | ||
if (nodesInChangedRanges.length > 1) { | ||
// Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter). | ||
textBlock = nodesInChangedRanges[0]; | ||
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, ' '); | ||
} | ||
else if (nodesInChangedRanges.length | ||
// We want to make sure to include the block seperator argument to treat hard breaks like spaces. | ||
&& newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')) { | ||
textBlock = nodesInChangedRanges[0]; | ||
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, ' '); | ||
} | ||
if (textBlock && textBeforeWhitespace) { | ||
const wordsBeforeWhitespace = textBeforeWhitespace.split(' ').filter(s => s !== ''); | ||
if (wordsBeforeWhitespace.length <= 0) { | ||
return false; | ||
} | ||
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1]; | ||
const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace); | ||
if (!lastWordBeforeSpace) { | ||
return false; | ||
} | ||
const linksBeforeSpace = tokenize(lastWordBeforeSpace).map(t => t.toObject(options.defaultProtocol)); | ||
if (!isValidLinkStructure(linksBeforeSpace)) { | ||
return false; | ||
} | ||
linksBeforeSpace | ||
.filter(link => link.isLink) | ||
// Calculate link position. | ||
.map(link => ({ | ||
...link, | ||
from: lastWordAndBlockOffset + link.start + 1, | ||
to: lastWordAndBlockOffset + link.end + 1, | ||
})) | ||
// ignore link inside code mark | ||
.filter(link => { | ||
if (!newState.schema.marks.code) { | ||
return true; | ||
} | ||
return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code); | ||
}) | ||
// validate link | ||
.filter(link => options.validate(link.value)) | ||
// Add link mark. | ||
.forEach(link => { | ||
if (getMarksBetween(link.from, link.to, newState.doc).some(item => item.mark.type === options.type)) { | ||
return; | ||
} | ||
tr.addMark(link.from, link.to, options.type.create({ | ||
href: link.href, | ||
})); | ||
}); | ||
} | ||
}); | ||
if (!tr.steps.length) { | ||
return; | ||
return !newState.doc.rangeHasMark( | ||
link.from, | ||
link.to, | ||
newState.schema.marks.code | ||
); | ||
}).filter((link) => options.validate(link.value)).forEach((link) => { | ||
if (getMarksBetween(link.from, link.to, newState.doc).some((item) => item.mark.type === options.type)) { | ||
return; | ||
} | ||
return tr; | ||
}, | ||
}); | ||
tr.addMark( | ||
link.from, | ||
link.to, | ||
options.type.create({ | ||
href: link.href | ||
}) | ||
); | ||
}); | ||
} | ||
}); | ||
if (!tr.steps.length) { | ||
return; | ||
} | ||
return tr; | ||
} | ||
}); | ||
} | ||
// src/helpers/clickHandler.ts | ||
import { getAttributes } from "@tiptap/core"; | ||
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state"; | ||
function clickHandler(options) { | ||
return new Plugin({ | ||
key: new PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a, _b; | ||
if (event.button !== 0) { | ||
return false; | ||
} | ||
if (!view.editable) { | ||
return false; | ||
} | ||
let a = event.target; | ||
const els = []; | ||
while (a.nodeName !== 'DIV') { | ||
els.push(a); | ||
a = a.parentNode; | ||
} | ||
if (!els.find(value => value.nodeName === 'A')) { | ||
return false; | ||
} | ||
const attrs = getAttributes(view.state, options.type.name); | ||
const link = event.target; | ||
const href = (_a = link === null || link === void 0 ? void 0 : link.href) !== null && _a !== void 0 ? _a : attrs.href; | ||
const target = (_b = link === null || link === void 0 ? void 0 : link.target) !== null && _b !== void 0 ? _b : attrs.target; | ||
if (link && href) { | ||
window.open(href, target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
}); | ||
return new Plugin2({ | ||
key: new PluginKey2("handleClickLink"), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a, _b; | ||
if (event.button !== 0) { | ||
return false; | ||
} | ||
if (!view.editable) { | ||
return false; | ||
} | ||
let a = event.target; | ||
const els = []; | ||
while (a.nodeName !== "DIV") { | ||
els.push(a); | ||
a = a.parentNode; | ||
} | ||
if (!els.find((value) => value.nodeName === "A")) { | ||
return false; | ||
} | ||
const attrs = getAttributes(view.state, options.type.name); | ||
const link = event.target; | ||
const href = (_a = link == null ? void 0 : link.href) != null ? _a : attrs.href; | ||
const target = (_b = link == null ? void 0 : link.target) != null ? _b : attrs.target; | ||
if (link && href) { | ||
window.open(href, target); | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
}); | ||
} | ||
// src/helpers/pasteHandler.ts | ||
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state"; | ||
import { find } from "linkifyjs"; | ||
function pasteHandler(options) { | ||
return new Plugin({ | ||
key: new PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = find(textContent, { defaultProtocol: options.defaultProtocol }).find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
options.editor.commands.setMark(options.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
}); | ||
return new Plugin3({ | ||
key: new PluginKey3("handlePasteLink"), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ""; | ||
slice.content.forEach((node) => { | ||
textContent += node.textContent; | ||
}); | ||
const link = find(textContent, { defaultProtocol: options.defaultProtocol }).find((item) => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
options.editor.commands.setMark(options.type, { | ||
href: link.href | ||
}); | ||
return true; | ||
} | ||
} | ||
}); | ||
} | ||
const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi; | ||
// From DOMPurify | ||
// https://github.com/cure53/DOMPurify/blob/main/src/regexp.js | ||
const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g; // eslint-disable-line no-control-regex | ||
const IS_ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; // eslint-disable-line no-useless-escape | ||
// src/link.ts | ||
var pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi; | ||
var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g; | ||
var IS_ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; | ||
function isAllowedUri(uri) { | ||
return !uri || uri.replace(ATTR_WHITESPACE, '').match(IS_ALLOWED_URI); | ||
return !uri || uri.replace(ATTR_WHITESPACE, "").match(IS_ALLOWED_URI); | ||
} | ||
/** | ||
* This extension allows you to create links. | ||
* @see https://www.tiptap.dev/api/marks/link | ||
*/ | ||
const Link = Mark.create({ | ||
name: 'link', | ||
priority: 1000, | ||
keepOnSplit: false, | ||
exitable: true, | ||
onCreate() { | ||
this.options.protocols.forEach(protocol => { | ||
if (typeof protocol === 'string') { | ||
registerCustomProtocol(protocol); | ||
return; | ||
} | ||
registerCustomProtocol(protocol.scheme, protocol.optionalSlashes); | ||
}); | ||
}, | ||
onDestroy() { | ||
reset(); | ||
}, | ||
inclusive() { | ||
return this.options.autolink; | ||
}, | ||
addOptions() { | ||
return { | ||
openOnClick: true, | ||
linkOnPaste: true, | ||
autolink: true, | ||
protocols: [], | ||
defaultProtocol: 'http', | ||
HTMLAttributes: { | ||
target: '_blank', | ||
rel: 'noopener noreferrer nofollow', | ||
class: null, | ||
}, | ||
validate: url => !!url, | ||
}; | ||
}, | ||
addAttributes() { | ||
return { | ||
href: { | ||
default: null, | ||
}, | ||
target: { | ||
default: this.options.HTMLAttributes.target, | ||
}, | ||
rel: { | ||
default: this.options.HTMLAttributes.rel, | ||
}, | ||
class: { | ||
default: this.options.HTMLAttributes.class, | ||
}, | ||
}; | ||
}, | ||
parseHTML() { | ||
return [{ | ||
tag: 'a[href]', | ||
getAttrs: dom => { | ||
const href = dom.getAttribute('href'); | ||
// prevent XSS attacks | ||
if (!href || !isAllowedUri(href)) { | ||
return false; | ||
} | ||
return { href }; | ||
}, | ||
}]; | ||
}, | ||
renderHTML({ HTMLAttributes }) { | ||
// prevent XSS attacks | ||
if (!isAllowedUri(HTMLAttributes.href)) { | ||
// strip out the href | ||
return ['a', mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, href: '' }), 0]; | ||
var Link = Mark.create({ | ||
name: "link", | ||
priority: 1e3, | ||
keepOnSplit: false, | ||
exitable: true, | ||
onCreate() { | ||
this.options.protocols.forEach((protocol) => { | ||
if (typeof protocol === "string") { | ||
registerCustomProtocol(protocol); | ||
return; | ||
} | ||
registerCustomProtocol(protocol.scheme, protocol.optionalSlashes); | ||
}); | ||
}, | ||
onDestroy() { | ||
reset(); | ||
}, | ||
inclusive() { | ||
return this.options.autolink; | ||
}, | ||
addOptions() { | ||
return { | ||
openOnClick: true, | ||
linkOnPaste: true, | ||
autolink: true, | ||
protocols: [], | ||
defaultProtocol: "http", | ||
HTMLAttributes: { | ||
target: "_blank", | ||
rel: "noopener noreferrer nofollow", | ||
class: null | ||
}, | ||
validate: (url) => !!url | ||
}; | ||
}, | ||
addAttributes() { | ||
return { | ||
href: { | ||
default: null, | ||
parseHTML(element) { | ||
return element.getAttribute("href"); | ||
} | ||
return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; | ||
}, | ||
addCommands() { | ||
return { | ||
setLink: attributes => ({ chain }) => { | ||
return chain().setMark(this.name, attributes).setMeta('preventAutolink', true).run(); | ||
}, | ||
toggleLink: attributes => ({ chain }) => { | ||
return chain() | ||
.toggleMark(this.name, attributes, { extendEmptyMarkRange: true }) | ||
.setMeta('preventAutolink', true) | ||
.run(); | ||
}, | ||
unsetLink: () => ({ chain }) => { | ||
return chain() | ||
.unsetMark(this.name, { extendEmptyMarkRange: true }) | ||
.setMeta('preventAutolink', true) | ||
.run(); | ||
}, | ||
}; | ||
}, | ||
addPasteRules() { | ||
return [ | ||
markPasteRule({ | ||
find: text => { | ||
const foundLinks = []; | ||
if (text) { | ||
const { validate } = this.options; | ||
const links = find(text).filter(item => item.isLink && validate(item.value)); | ||
if (links.length) { | ||
links.forEach(link => (foundLinks.push({ | ||
text: link.value, | ||
data: { | ||
href: link.href, | ||
}, | ||
index: link.start, | ||
}))); | ||
} | ||
} | ||
return foundLinks; | ||
}, | ||
target: { | ||
default: this.options.HTMLAttributes.target | ||
}, | ||
rel: { | ||
default: this.options.HTMLAttributes.rel | ||
}, | ||
class: { | ||
default: this.options.HTMLAttributes.class | ||
} | ||
}; | ||
}, | ||
parseHTML() { | ||
return [{ | ||
tag: "a[href]", | ||
getAttrs: (dom) => { | ||
const href = dom.getAttribute("href"); | ||
if (!href || !isAllowedUri(href)) { | ||
return false; | ||
} | ||
return null; | ||
} | ||
}]; | ||
}, | ||
renderHTML({ HTMLAttributes }) { | ||
if (!isAllowedUri(HTMLAttributes.href)) { | ||
return ["a", mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, href: "" }), 0]; | ||
} | ||
return ["a", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; | ||
}, | ||
addCommands() { | ||
return { | ||
setLink: (attributes) => ({ chain }) => { | ||
return chain().setMark(this.name, attributes).setMeta("preventAutolink", true).run(); | ||
}, | ||
toggleLink: (attributes) => ({ chain }) => { | ||
return chain().toggleMark(this.name, attributes, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run(); | ||
}, | ||
unsetLink: () => ({ chain }) => { | ||
return chain().unsetMark(this.name, { extendEmptyMarkRange: true }).setMeta("preventAutolink", true).run(); | ||
} | ||
}; | ||
}, | ||
addPasteRules() { | ||
return [ | ||
markPasteRule({ | ||
find: (text) => { | ||
const foundLinks = []; | ||
if (text) { | ||
const { validate } = this.options; | ||
const links = find2(text).filter((item) => item.isLink && validate(item.value)); | ||
if (links.length) { | ||
links.forEach((link) => foundLinks.push({ | ||
text: link.value, | ||
data: { | ||
href: link.href | ||
}, | ||
type: this.type, | ||
getAttributes: match => { | ||
var _a; | ||
return { | ||
href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href, | ||
}; | ||
}, | ||
}), | ||
]; | ||
}, | ||
addProseMirrorPlugins() { | ||
const plugins = []; | ||
if (this.options.autolink) { | ||
plugins.push(autolink({ | ||
type: this.type, | ||
defaultProtocol: this.options.defaultProtocol, | ||
validate: this.options.validate, | ||
})); | ||
index: link.start | ||
})); | ||
} | ||
} | ||
return foundLinks; | ||
}, | ||
type: this.type, | ||
getAttributes: (match) => { | ||
var _a; | ||
return { | ||
href: (_a = match.data) == null ? void 0 : _a.href | ||
}; | ||
} | ||
if (this.options.openOnClick === true) { | ||
plugins.push(clickHandler({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push(pasteHandler({ | ||
editor: this.editor, | ||
defaultProtocol: this.options.defaultProtocol, | ||
type: this.type, | ||
})); | ||
} | ||
return plugins; | ||
}, | ||
}) | ||
]; | ||
}, | ||
addProseMirrorPlugins() { | ||
const plugins = []; | ||
if (this.options.autolink) { | ||
plugins.push( | ||
autolink({ | ||
type: this.type, | ||
defaultProtocol: this.options.defaultProtocol, | ||
validate: this.options.validate | ||
}) | ||
); | ||
} | ||
if (this.options.openOnClick === true) { | ||
plugins.push( | ||
clickHandler({ | ||
type: this.type | ||
}) | ||
); | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push( | ||
pasteHandler({ | ||
editor: this.editor, | ||
defaultProtocol: this.options.defaultProtocol, | ||
type: this.type | ||
}) | ||
); | ||
} | ||
return plugins; | ||
} | ||
}); | ||
export { Link, Link as default, pasteRegex }; | ||
//# sourceMappingURL=index.js.map | ||
// src/index.ts | ||
var src_default = Link; | ||
export { | ||
Link, | ||
src_default as default, | ||
pasteRegex | ||
}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@tiptap/extension-link", | ||
"description": "link extension for tiptap", | ||
"version": "3.0.0-next.0", | ||
"version": "3.0.0-next.1", | ||
"homepage": "https://tiptap.dev", | ||
@@ -18,3 +18,3 @@ "keywords": [ | ||
".": { | ||
"types": "./dist/packages/extension-link/src/index.d.ts", | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.js", | ||
@@ -26,4 +26,3 @@ "require": "./dist/index.cjs" | ||
"module": "dist/index.js", | ||
"umd": "dist/index.umd.js", | ||
"types": "dist/packages/extension-link/src/index.d.ts", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
@@ -37,8 +36,8 @@ "src", | ||
"devDependencies": { | ||
"@tiptap/core": "^3.0.0-next.0", | ||
"@tiptap/pm": "^3.0.0-next.0" | ||
"@tiptap/core": "^3.0.0-next.1", | ||
"@tiptap/pm": "^3.0.0-next.1" | ||
}, | ||
"peerDependencies": { | ||
"@tiptap/core": "^3.0.0-next.0", | ||
"@tiptap/pm": "^3.0.0-next.0" | ||
"@tiptap/core": "^3.0.0-next.1", | ||
"@tiptap/pm": "^3.0.0-next.1" | ||
}, | ||
@@ -51,5 +50,4 @@ "repository": { | ||
"scripts": { | ||
"clean": "rm -rf dist", | ||
"build": "npm run clean && rollup -c" | ||
"build": "tsup" | ||
} | ||
} |
@@ -167,2 +167,5 @@ import { | ||
default: null, | ||
parseHTML(element) { | ||
return element.getAttribute('href') | ||
}, | ||
}, | ||
@@ -191,3 +194,3 @@ target: { | ||
} | ||
return { href } | ||
return null | ||
}, | ||
@@ -194,0 +197,0 @@ }] |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91607
13
1280
1