diff --git a/nip27.ts b/nip27.ts index c63804b..f8a6d3b 100644 --- a/nip27.ts +++ b/nip27.ts @@ -13,7 +13,7 @@ export interface NostrURIMatch extends NostrURI { } /** Find and decode all NIP-21 URIs. */ -export function* matchAll(content: string): Iterable { +export function * matchAll(content: string): Iterable { const matches = content.matchAll(regex()) for (const match of matches) { diff --git a/nip30.test.ts b/nip30.test.ts new file mode 100644 index 0000000..cc7809f --- /dev/null +++ b/nip30.test.ts @@ -0,0 +1,32 @@ +import {matchAll, replaceAll} from './nip30.ts' + +test('matchAll', () => { + const result = matchAll('Hello :blobcat: :disputed: ::joy:joy:') + + expect([...result]).toEqual([ + { + name: 'blobcat', + shortcode: ':blobcat:', + start: 6, + end: 15 + }, + { + name: 'disputed', + shortcode: ':disputed:', + start: 16, + end: 26 + } + ]) +}) + +test('replaceAll', () => { + const content = 'Hello :blobcat: :disputed: ::joy:joy:' + + const result = replaceAll(content, ({name}) => { + return `` + }) + + expect(result).toEqual( + 'Hello ::joy:joy:', + ) +}) diff --git a/nip30.ts b/nip30.ts new file mode 100644 index 0000000..8465c85 --- /dev/null +++ b/nip30.ts @@ -0,0 +1,54 @@ +/** Regex for a single emoji shortcode. */ +export const EMOJI_SHORTCODE_REGEX = /:(\w+):/ + +/** Regex to find emoji shortcodes in content. */ +export const regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, 'g') + +/** Represents a Nostr custom emoji. */ +export interface CustomEmoji { + /** The matched emoji name with colons. */ + shortcode: `:${string}:` + /** The matched emoji name without colons. */ + name: string +} + +/** Match result for a custom emoji in text content. */ +export interface CustomEmojiMatch extends CustomEmoji { + /** Index where the emoji begins in the text content. */ + start: number + /** Index where the emoji ends in the text content. */ + end: number +} + +/** Find all custom emoji shortcodes. */ +export function * matchAll(content: string): Iterable { + const matches = content.matchAll(regex()) + + for (const match of matches) { + try { + const [shortcode, name] = match + + yield { + shortcode: shortcode as `:${string}:`, + name, + start: match.index!, + end: match.index! + shortcode.length + } + } catch (_e) { + // do nothing + } + } +} + +/** Replace all emoji shortcodes in the content. */ +export function replaceAll( + content: string, + replacer: (match: CustomEmoji) => string +): string { + return content.replaceAll(regex(), (shortcode, name) => { + return replacer({ + shortcode: shortcode as `:${string}:`, + name, + }) + }) +}