├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.1.1 2 | ## Fixes 3 | - Hash link as internal URL, thanks @samiahmedsiddiqui. 4 | 5 | # 1.0.2 6 | ## Improvements 7 | - Code styles fixes, thanks @DmitryGrigorov. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vitaliy Bobrov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # remarkable-extlink 🔗 2 | [![npm version](https://badge.fury.io/js/remarkable-extlink.svg)](https://badge.fury.io/js/remarkable-extlink) 3 | [![npm](https://img.shields.io/npm/dt/remarkable-extlink.svg)](https://github.com/vitaliy-bobrov/remarkable-extlink) 4 | 5 | [Remarkable](https://github.com/jonschlinkert/remarkable) plugin adds `target` and `rel` attributes for external links. 6 | 7 | ## Installation 8 | - npm: 9 | `npm install --save-dev remarkable-extlink` 10 | 11 | - yarn: 12 | `yarn add -D remarkable-extlink` 13 | 14 | ## Usage 15 | ```js 16 | const Remarkable = require('remarkable'); 17 | const extLink = require('remarkable-extlink'); 18 | const md = new Remarkable(); 19 | 20 | md 21 | .use(extlink, { 22 | host: 'my-host.com' 23 | }); 24 | ``` 25 | 26 | ## Options 27 | 28 | ### host {String} 29 | 30 | **Required** 31 | 32 | You site host name to detect external links. 33 | 34 | ### target {String} 35 | 36 | Target link attribute value, default `_blank`. 37 | 38 | ### rel {String} 39 | 40 | Rel link attribute value, default `nofollow noreferrer noopener`. 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); 2 | 3 | var DEFAULT_OPTIONS = { 4 | target: '_blank', 5 | rel: 'nofollow noreferrer noopener' 6 | }; 7 | 8 | /** 9 | * Defines link is internal. 10 | * @param host {String} Site hostname. 11 | * @param href {Object} Parsed url object. 12 | * @return {Boolean} 13 | */ 14 | var isInternal = function (host, href) { 15 | return href.host === host || (!href.protocol && !href.host && (href.pathname || href.hash)); 16 | }; 17 | 18 | var remarkableExtLink = function (md, options) { 19 | var config = Object.assign({}, DEFAULT_OPTIONS, options); 20 | 21 | // Parse and normalize hostname. 22 | config.host = url.parse(config.host).host; 23 | // Save original method to invoke. 24 | var originalRender = md.renderer.rules.link_open; 25 | 26 | md.renderer.rules.link_open = function() { 27 | var result; 28 | 29 | // Invoke original method first. 30 | result = originalRender.apply(null, arguments); 31 | 32 | var regexp = /href="([^"]*)"/; 33 | 34 | var href = url.parse(regexp.exec(result)[1]); 35 | 36 | if (!isInternal(config.host, href)) { 37 | result = result.replace('>', ' target="' + config.target + '" rel="' + config.rel + '">'); 38 | } 39 | 40 | return result; 41 | }; 42 | }; 43 | 44 | module.exports = remarkableExtLink; 45 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remarkable-extlink", 3 | "version": "1.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "argparse": { 8 | "version": "1.0.10", 9 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 10 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 11 | "requires": { 12 | "sprintf-js": "~1.0.2" 13 | } 14 | }, 15 | "autolinker": { 16 | "version": "3.14.1", 17 | "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.14.1.tgz", 18 | "integrity": "sha512-yvsRHIaY51EYDml6MGlbqyJGfl4n7zezGYf+R7gvM8c5LNpRGc4SISkvgAswSS8SWxk/OrGCylKV9mJyVstz7w==", 19 | "requires": { 20 | "tslib": "^1.9.3" 21 | } 22 | }, 23 | "punycode": { 24 | "version": "1.3.2", 25 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 26 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 27 | }, 28 | "querystring": { 29 | "version": "0.2.0", 30 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 31 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 32 | }, 33 | "remarkable": { 34 | "version": "2.0.1", 35 | "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz", 36 | "integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==", 37 | "requires": { 38 | "argparse": "^1.0.10", 39 | "autolinker": "^3.11.0" 40 | } 41 | }, 42 | "sprintf-js": { 43 | "version": "1.0.3", 44 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 45 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 46 | }, 47 | "tslib": { 48 | "version": "1.13.0", 49 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 50 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" 51 | }, 52 | "url": { 53 | "version": "0.11.0", 54 | "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", 55 | "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", 56 | "requires": { 57 | "punycode": "1.3.2", 58 | "querystring": "0.2.0" 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remarkable-extlink", 3 | "version": "1.1.1", 4 | "description": "Adds target and rel attributes to external links.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/vitaliy-bobrov/remarkable-extlink.git" 12 | }, 13 | "keywords": [ 14 | "remarkable", 15 | "plugin", 16 | "extlink", 17 | "external", 18 | "links" 19 | ], 20 | "author": "Vitaliy Bobrov", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/vitaliy-bobrov/remarkable-extlink/issues" 24 | }, 25 | "homepage": "https://github.com/vitaliy-bobrov/remarkable-extlink#readme", 26 | "dependencies": { 27 | "remarkable": "^2.0.1", 28 | "url": "^0.11.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | argparse@^1.0.10: 6 | version "1.0.10" 7 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 8 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 9 | dependencies: 10 | sprintf-js "~1.0.2" 11 | 12 | autolinker@^3.11.0: 13 | version "3.14.1" 14 | resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.14.1.tgz#6ae4b812b6eaf42d4d68138b9e67757cbf2bc1e4" 15 | integrity sha512-yvsRHIaY51EYDml6MGlbqyJGfl4n7zezGYf+R7gvM8c5LNpRGc4SISkvgAswSS8SWxk/OrGCylKV9mJyVstz7w== 16 | dependencies: 17 | tslib "^1.9.3" 18 | 19 | punycode@1.3.2: 20 | version "1.3.2" 21 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" 22 | 23 | querystring@0.2.0: 24 | version "0.2.0" 25 | resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 26 | 27 | remarkable@^2.0.1: 28 | version "2.0.1" 29 | resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31" 30 | integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA== 31 | dependencies: 32 | argparse "^1.0.10" 33 | autolinker "^3.11.0" 34 | 35 | sprintf-js@~1.0.2: 36 | version "1.0.3" 37 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 38 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 39 | 40 | tslib@^1.9.3: 41 | version "1.13.0" 42 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" 43 | integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== 44 | 45 | url@^0.11.0: 46 | version "0.11.0" 47 | resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" 48 | dependencies: 49 | punycode "1.3.2" 50 | querystring "0.2.0" 51 | --------------------------------------------------------------------------------