diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa6df2d6..20175f0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
### Changed
+- Change CDN for Twemoji images from default to jsDelivr ([#320](https://github.com/marp-team/marp-core/issues/320), [#321](https://github.com/marp-team/marp-core/pull/321))
- Upgrade Marpit to [v2.4.2](https://github.com/marp-team/marpit/releases/v2.4.2) ([#318](https://github.com/marp-team/marp-core/pull/318))
- Upgrade development Node.js and dependent packages ([#318](https://github.com/marp-team/marp-core/pull/318))
diff --git a/README.md b/README.md
index 573097df..738a79b7 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ Theme author does not have to worry an unintended design being used with unexpec
### Emoji support
-Emoji shortcode (like `:smile:`) and Unicode emoji 😄 will convert into the SVG vector image provided by [twemoji](https://github.com/twitter/twemoji) . It could render emoji with high resolution.
+Emoji shortcode (like `:smile:`) and Unicode emoji 😄 will convert into the SVG vector image provided by [twemoji](https://github.com/twitter/twemoji) . It could render emoji with high resolution.
---
@@ -275,14 +275,14 @@ Setting about emoji conversions.
- **`shortcode`**: _`boolean` | `"twemoji"`_
- By setting `false`, it does not convert any emoji shortcodes.
- By setting `true`, it converts emoji shortcodes into Unicode emoji. `:dog:` → 🐶
- - By setting `"twemoji"` string, it converts into twemoji vector image. `:dog:` → _(default)_
+ - By setting `"twemoji"` string, it converts into twemoji vector image. `:dog:` → _(default)_
* **`unicode`**: _`boolean` | `"twemoji"`_
- - It can convert Unicode emoji into twemoji when setting `"twemoji"`. 🐶 → _(default)_
+ - It can convert Unicode emoji into twemoji when setting `"twemoji"`. 🐶 → _(default)_
- If you not want this aggressive conversion, please set `false`.
- **`twemoji`**: _`object`_
- - **`base`**: _`string`_ - It is corresponded to [twemoji's `base` option](https://github.com/twitter/twemoji#object-as-parameter). By default, marp-core will use online emoji images [through MaxCDN (twemoji's default)](https://github.com/twitter/twemoji#cdn-support).
+ - **`base`**: _`string`_ - Corresponds to [twemoji's `base` option](https://github.com/twitter/twemoji#object-as-parameter). If not specified, Marp Core will use [online emoji images through jsDelivr CDN](https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/).
- **`ext`**: _`"svg"` | `"png"`_ - Setting the file type of twemoji images. _(`svg` by default)_
> **For developers:** When you setting `unicode` option as `true`, Markdown parser will convert Unicode emoji into tokens internally. The rendering result is same as in `false`.
diff --git a/src/emoji/emoji.ts b/src/emoji/emoji.ts
index 3e4e1120..38465074 100644
--- a/src/emoji/emoji.ts
+++ b/src/emoji/emoji.ts
@@ -2,6 +2,7 @@ import marpitPlugin from '@marp-team/marpit/plugin'
import emojiRegex from 'emoji-regex'
import markdownItEmoji from 'markdown-it-emoji'
import twemoji from 'twemoji'
+import { version as twemojiVersion } from 'twemoji/package.json'
import twemojiCSS from './twemoji.scss'
export interface EmojiOptions {
@@ -30,10 +31,16 @@ export const markdown = marpitPlugin((md) => {
const twemojiParse = (content: string): string =>
twemoji.parse(content, {
attributes: () => ({ 'data-marp-twemoji': '' }),
- base: twemojiOpts.base || undefined,
+ // Twemoji's default CDN (MaxCDN) shuts down at December 31, 2022.
+ // Unfortunately, continuous updates of Twemoji (including the update of
+ // base path) can not be expected due to Elon's acquisition for now. So
+ // Marp uses the CDN of jsDelivr unless the user specifies the base path.
+ base: Object.hasOwnProperty.call(twemojiOpts, 'base')
+ ? twemojiOpts.base
+ : `https://cdn.jsdelivr.net/gh/twitter/twemoji@${twemojiVersion}/assets/`,
ext: `.${twemojiExt}`,
size: twemojiExt === 'svg' ? 'svg' : undefined,
- }) as any // TODO: Remove any casting (https://github.com/twitter/twemoji/pull/535)
+ })
const twemojiRenderer = (token: any[], idx: number): string =>
twemojiParse(token[idx].content)
diff --git a/test/marp.ts b/test/marp.ts
index e9e5c8da..b5e045a4 100644
--- a/test/marp.ts
+++ b/test/marp.ts
@@ -162,26 +162,37 @@ describe('Marp', () => {
const instance = (twemoji: EmojiOptions['twemoji'] = {}) =>
new Marp({ emoji: { twemoji } })
- it('uses SVG via twemoji CDN by default', () => {
- const $ = load(instance().render('# :ok_hand:').html)
- const src = $('h1 > img[data-marp-twemoji]').attr('src')
+ const emojiSrc = (emoji: string, marp = instance()) => {
+ const $ = load(marp.render(`# ${emoji}`).html)
+ return $('h1 > img').attr('src')
+ }
- expect(src).toMatchInlineSnapshot(
- `"https://twemoji.maxcdn.com/v/14.0.2/svg/1f44c.svg"`
+ it('uses SVG via jsDelivr CDN by default', () => {
+ expect(emojiSrc(':ok_hand:')).toMatchInlineSnapshot(
+ `"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/1f44c.svg"`
)
})
describe('base option', () => {
it('uses specified base', () =>
expect(
- instance({ base: '/assets/twemoji/' }).render(':+1:').html
- ).toContain('/assets/twemoji/svg/1f44d.svg'))
+ emojiSrc(':+1:', instance({ base: '/assets/twemoji/' }))
+ ).toMatchInlineSnapshot(`"/assets/twemoji/svg/1f44d.svg"`))
+
+ it("uses Twemoji's default CDN if the base option was undefined", () =>
+ expect(
+ emojiSrc(':+1:', instance({ base: undefined }))
+ ).toMatchInlineSnapshot(
+ `"https://twemoji.maxcdn.com/v/14.0.2/svg/1f44d.svg"`
+ ))
})
describe('ext option', () => {
it('uses PNG emoji by setting png', () =>
- expect(instance({ ext: 'png' }).render(':+1:').html).toMatch(
- /https:\/\/twemoji\.maxcdn\.com\/[\w/.]+\/1f44d\.png/
+ expect(
+ emojiSrc(':+1:', instance({ ext: 'png' }))
+ ).toMatchInlineSnapshot(
+ `"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f44d.png"`
))
})
})