Skip to content

Commit

Permalink
feat: allow to truncate long link text and remove protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
robertu7 committed Jun 13, 2024
1 parent 7f4649a commit f63c575
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 16 deletions.
33 changes: 30 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@matters/matters-editor",
"version": "0.2.5-alpha.5",
"version": "0.2.5-alpha.6",
"description": "Editor for matters.news",
"author": "https://github.com/thematters",
"homepage": "https://github.com/thematters/matters-editor",
Expand Down Expand Up @@ -74,6 +74,7 @@
"remark-rehype": "^11.1.0",
"remark-stringify": "^11.0.0",
"unified": "^11.0.4",
"validator": "^13.12.0",
"zeed-dom": "^0.13.3"
},
"devDependencies": {
Expand All @@ -91,6 +92,7 @@
"@types/node": "^20.11.25",
"@types/react": "^17.0.53",
"@types/react-dom": "^17.0.19",
"@types/validator": "^13.11.10",
"benny": "^3.7.1",
"common-tags": "^1.8.2",
"eslint": "^9.4.0",
Expand Down
46 changes: 40 additions & 6 deletions src/transformers/normalize.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import { stripIndent } from 'common-tags'
import { describe, expect, test } from 'vitest'

import { normalizeArticleHTML, normalizeCommentHTML } from './normalize'

const expectNormalizeArticleHTML = (input: string, output: string) => {
const result = normalizeArticleHTML(input)
import {
normalizeArticleHTML,
normalizeCommentHTML,
NormalizeOptions,
} from './normalize'

const expectNormalizeArticleHTML = (
input: string,
output: string,
options?: NormalizeOptions,
) => {
const result = normalizeArticleHTML(input, options)
expect(result.trim()).toBe(output)
}

const expectNormalizeCommentHTML = (input: string, output: string) => {
const result = normalizeCommentHTML(input)
const expectNormalizeCommentHTML = (
input: string,
output: string,
options?: NormalizeOptions,
) => {
const result = normalizeCommentHTML(input, options)
expect(result.trim()).toBe(output)
}

Expand Down Expand Up @@ -318,6 +330,28 @@ describe('Normalization: Comment', () => {
'<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://example.com">abc</a></p>',
'<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://example.com">abc</a></p>',
)

const longURL =
'https://medium.com/yihan-huang-studio/%E4%BA%BA%E6%A0%BC%E6%8A%BD%E9%9B%A2%E7%9A%84%E5%B9%BB%E8%A6%BA%E6%B0%A3%E5%91%B3-%E4%BB%A5%E9%B4%89%E7%89%87%E5%85%A5%E9%A6%99%E7%9A%84-boudicca-wode-630a5b253bb3'

expectNormalizeCommentHTML(
`<p><a target="_blank" rel="noopener noreferrer nofollow" href="${longURL}">${longURL}</a></p>`,
`<p><a target="_blank" rel="noopener noreferrer nofollow" href="${longURL}">medium.com/yihan-hua...</a></p>`,
{ truncate: { maxLength: 20, keepProtocol: false } },
)

expectNormalizeCommentHTML(
`<p><a class="mention" rel="noopener noreferrer nofollow" href="${longURL}">${longURL}</a></p>`,
`<p><a class="mention" rel="noopener noreferrer nofollow" href="${longURL}">https://medium.com/y...</a></p>`,
{ truncate: { maxLength: 20, keepProtocol: true } },
)

expect(() =>
normalizeCommentHTML(
`<p><a target="_blank" rel="noopener noreferrer nofollow" href="${longURL}">${longURL}</a></p>`,
{ truncate: { maxLength: 0, keepProtocol: true } },
),
).toThrow('maxLength must be greater than 0')
})

test('bolds is not supported', () => {
Expand Down
80 changes: 74 additions & 6 deletions src/transformers/normalize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Extensions } from '@tiptap/core'
import { getSchema } from '@tiptap/core'
import { DOMParser, DOMSerializer, Node } from '@tiptap/pm/model'
import isURL from 'validator/lib/isURL'
import { createHTMLDocument, parseHTML, type VHTMLDocument } from 'zeed-dom'

import {
Expand All @@ -10,6 +11,13 @@ import {
Mention,
} from '../editors/extensions'

export type NormalizeOptions = {
truncate?: {
maxLength: number
keepProtocol: boolean
}
}

export const makeNormalizer = (extensions: Extensions) => {
const schema = getSchema(extensions)

Expand All @@ -30,20 +38,80 @@ export const makeNormalizer = (extensions: Extensions) => {
}
}

export const normalizeArticleHTML = (html: string): string => {
// match HTML anchor tags and truncate the text
export const truncateLinkText = (
html: string,
{ maxLength, keepProtocol }: { maxLength: number; keepProtocol: boolean },
): string => {
const regex = /<a\s+([^>]*?)>(.*?)<\/a>/gi

return html.replace(regex, (match, attributes: string, text: string) => {
if (!isURL(text)) {
return match
}

let truncatedText = text

if (!keepProtocol) {
truncatedText = text.replace(/(^\w+:|^)\/\//, '')
}

if (maxLength === 0) {
throw new Error('maxLength must be greater than 0')
}

if (maxLength && truncatedText.length > maxLength) {
truncatedText = truncatedText.slice(0, maxLength) + '...'
}

return `<a ${attributes}>${truncatedText}</a>`
})
}

export const normalizeArticleHTML = (
html: string,
options?: NormalizeOptions,
): string => {
const extensions = makeArticleEditorExtensions({})
const normalizer = makeNormalizer([...extensions, Mention])
return normalizer(html)

let normalizedHtml = normalizer(html)

if (options?.truncate) {
normalizedHtml = truncateLinkText(html, options.truncate)
}

return normalizedHtml
}

export const normalizeCommentHTML = (html: string): string => {
export const normalizeCommentHTML = (
html: string,
options?: NormalizeOptions,
): string => {
const extensions = makeCommentEditorExtensions({})
const normalizer = makeNormalizer([...extensions, Mention])
return normalizer(html)

let normalizedHtml = normalizer(html)

if (options?.truncate) {
normalizedHtml = truncateLinkText(html, options.truncate)
}

return normalizedHtml
}

export const normalizeJournalHTML = (html: string): string => {
export const normalizeJournalHTML = (
html: string,
options?: NormalizeOptions,
): string => {
const extensions = makeJournalEditorExtensions({})
const normalizer = makeNormalizer([...extensions, Mention])
return normalizer(html)

let normalizedHtml = normalizer(html)

if (options?.truncate) {
normalizedHtml = truncateLinkText(html, options.truncate)
}

return normalizedHtml
}

0 comments on commit f63c575

Please sign in to comment.