diff --git a/CHANGELOG.md b/CHANGELOG.md index 0270853..5315892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.5.0 (2022-12-18) + +### ✨ Features + + * Add autolinker plugins + +Credits + +* [@Bunlong](https://github.com/Bunlong) + ## 0.4.0 (2022-12-17) ### ✨ Features diff --git a/README.md b/README.md index 5a179e0..7c47358 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,11 @@ export default function App() { const { Code } = usePrism() return ( - <> - + {`
{Math.random()}
`} -
- +
) } ``` @@ -68,13 +66,9 @@ function App() { return ( <> - +

You clicked {count} times

- - {`

You clicked ${count} times

`} - + {`

You clicked ${count} times

`} ) } @@ -777,13 +771,11 @@ export default function App() { const { Code } = usePrism() return ( - <> - + {`

{Math.random()}
`} - - + ) } ``` @@ -807,22 +799,48 @@ export default function App() { const { Code } = usePrism() return ( - <> - + {`
- {Math.random()} +{Math.random()}
`} -
- +
) } ``` +### Autolinker + +Converts URLs and emails in code to clickable links. Parses Markdown links in comments. + +```javascript +import { usePrism } from 'next-prism' + +// Import a theme.css +import 'next-prism/themes/tomorrow.css' + +// Import autolinker source +import 'next-prism/plugins/autolinker/autolinker' +// Import autolinker.css +import 'next-prism/plugins/autolinker/autolinker.css' + +function App() { + const { Code } = usePrism() + + return ( + + {`
+ next-prism +
`} +
+ ); +} +``` + ## 📜 Changelog -Latest version 0.4.0 (2022-12-17): +Latest version 0.5.0 (2022-12-18): - * Add show-invisibles plugins + * Add autolinker plugins Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/next-prism/blob/master/CHANGELOG.md). diff --git a/package.json b/package.json index 29a71bc..c9219ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-prism", - "version": "0.4.1", + "version": "0.5.0", "description": "A lightweight, robust, and elegant syntax highlighting component for your next React apps.", "author": "Bunlong ", "license": "MIT", diff --git a/plugins/autolinker/autolinker.css b/plugins/autolinker/autolinker.css new file mode 100644 index 0000000..8ef93a0 --- /dev/null +++ b/plugins/autolinker/autolinker.css @@ -0,0 +1,3 @@ +.token a { + color: inherit; +} diff --git a/plugins/autolinker/autolinker.js b/plugins/autolinker/autolinker.js new file mode 100644 index 0000000..532e11b --- /dev/null +++ b/plugins/autolinker/autolinker.js @@ -0,0 +1,76 @@ +(function () { + + if (typeof Prism === 'undefined') { + return; + } + + var url = /\b([a-z]{3,7}:\/\/|tel:)[\w\-+%~/.:=&!$'()*,;@]+(?:\?[\w\-+%~/.:=?&!$'()*,;@]*)?(?:#[\w\-+%~/.:#=?&!$'()*,;@]*)?/; + var email = /\b\S+@[\w.]+[a-z]{2}/; + var linkMd = /\[([^\]]+)\]\(([^)]+)\)/; + + // Tokens that may contain URLs and emails + var candidates = ['comment', 'url', 'attr-value', 'string']; + + Prism.plugins.autolinker = { + processGrammar: function (grammar) { + // Abort if grammar has already been processed + if (!grammar || grammar['url-link']) { + return; + } + Prism.languages.DFS(grammar, function (key, def, type) { + if (candidates.indexOf(type) > -1 && !Array.isArray(def)) { + if (!def.pattern) { + def = this[key] = { + pattern: def + }; + } + + def.inside = def.inside || {}; + + if (type == 'comment') { + def.inside['md-link'] = linkMd; + } + if (type == 'attr-value') { + Prism.languages.insertBefore('inside', 'punctuation', { 'url-link': url }, def); + } else { + def.inside['url-link'] = url; + } + + def.inside['email-link'] = email; + } + }); + grammar['url-link'] = url; + grammar['email-link'] = email; + } + }; + + Prism.hooks.add('before-highlight', function (env) { + Prism.plugins.autolinker.processGrammar(env.grammar); + }); + + Prism.hooks.add('wrap', function (env) { + if (/-link$/.test(env.type)) { + env.tag = 'a'; + + var href = env.content; + + if (env.type == 'email-link' && href.indexOf('mailto:') != 0) { + href = 'mailto:' + href; + } else if (env.type == 'md-link') { + // Markdown + var match = env.content.match(linkMd); + + href = match[2]; + env.content = match[1]; + } + + env.attributes.href = href; + + // Silently catch any error thrown by decodeURIComponent (#1186) + try { + env.content = decodeURIComponent(env.content); + } catch (e) { /* noop */ } + } + }); + +}()); diff --git a/supports/create-react-app/src/App.js b/supports/create-react-app/src/App.js index 38b376b..60392c5 100644 --- a/supports/create-react-app/src/App.js +++ b/supports/create-react-app/src/App.js @@ -1,29 +1,19 @@ -import { useState, useEffect } from 'react'; import { usePrism } from 'next-prism'; import 'next-prism/themes/tomorrow.css'; -import 'next-prism/plugins/show-invisibles/show-invisibles.css'; -import 'next-prism/plugins/show-invisibles/show-invisibles'; +import 'next-prism/plugins/autolinker/autolinker'; +import 'next-prism/plugins/autolinker/autolinker.css'; function App() { - const [count, setCount] = useState(0); - const { Code, highlightAll } = usePrism(); - - useEffect(() => { - highlightAll(); - }, [count]); + const { Code } = usePrism(); return ( - <> - -

You clicked {count} times

- - {`

You clicked ${count} times

`} - - + + {`

+ next-prism +
`} + ); }