Skip to content

Commit

Permalink
Require Node.js 8
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Dec 9, 2018
1 parent 5d04065 commit cc77665
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 42 deletions.
25 changes: 15 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
'use strict';
const createHtmlElement = require('create-html-element');

// Capture the whole URL in group 1 to keep string.split() support
// Capture the whole URL in group 1 to keep `String#split()` support
const urlRegex = () => (/((?<!\+)(?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#!?&//=@]*)(?:[,](?![\s]))*)*)/g);

// Get <a> element as string
// Get `<a>` element as string
const linkify = (href, options) => createHtmlElement({
name: 'a',
attributes: Object.assign({href: ''}, options.attributes, {href}),
attributes: {
href: '',
...options.attributes,
href // eslint-disable-line no-dupe-keys
},
text: typeof options.value === 'undefined' ? href : undefined,
html: typeof options.value === 'undefined' ? undefined :
(typeof options.value === 'function' ? options.value(href) : options.value)
Expand All @@ -21,22 +25,23 @@ const getAsString = (input, options) => {
};

const getAsDocumentFragment = (input, options) => {
return input.split(urlRegex()).reduce((frag, text, index) => {
return input.split(urlRegex()).reduce((fragment, text, index) => {
if (index % 2) { // URLs are always in odd positions
frag.appendChild(domify(linkify(text, options)));
fragment.appendChild(domify(linkify(text, options)));
} else if (text.length > 0) {
frag.appendChild(document.createTextNode(text));
fragment.appendChild(document.createTextNode(text));
}

return frag;
return fragment;
}, document.createDocumentFragment());
};

module.exports = (input, options) => {
options = Object.assign({
options = {
attributes: {},
type: 'string'
}, options);
type: 'string',
...options
};

if (options.type === 'string') {
return getAsString(input, options);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"url": "sindresorhus.com"
},
"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"test": "xo && ava"
Expand All @@ -33,7 +33,7 @@
},
"devDependencies": {
"ava": "^0.25.0",
"jsdom": "^11.3.0",
"jsdom": "^13.0.0",
"xo": "^0.23.0"
},
"xo": {
Expand Down
7 changes: 3 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,10 @@ linkifyUrls('See https://sindresorhus.com/foo', {
//=> 'See <a href="https://sindresorhus.com/foo">/foo</a>'
```

## Compatibility note

Since v2.2.0, the main regular expression is using a negative look-behind to address [#7](https://github.com/sindresorhus/linkify-urls/issues/7).
Look-behind assertions [should be compatible since Node v6](https://node.green/#ES2018-features--RegExp-Lookbehind-Assertions) (with the use of `--harmony` flag).
At the time of this note, only Google Chrome seems to support it, even though it [will eventually be a standard](https://tc39.github.io/ecma262/).
## Browser compatibility

Version 3 of this package uses [negative lookbehind regex syntax](http://kangax.github.io/compat-table/es2016plus/#test-RegExp_Lookbehind_Assertions). Stay on version 2 if you need to support browsers that doesn't support this feature.


## Related
Expand Down
52 changes: 26 additions & 26 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {URL} from 'url';
import test from 'ava';
import jsdom from 'jsdom';
import m from '.';
import linkifyUrls from '.';

const dom = new jsdom.JSDOM();
global.window = dom.window;
Expand All @@ -11,9 +11,9 @@ global.document = dom.window.document;
// https://github.com/tmpvar/jsdom/issues/317
document.createRange = () => ({
createContextualFragment(html) {
const el = document.createElement('template');
el.innerHTML = html;
return el.content;
const element = document.createElement('template');
element.innerHTML = html;
return element.content;
}
});

Expand All @@ -29,12 +29,12 @@ const html = dom => {

test('main', t => {
t.is(
m('See https://sindresorhus.com and https://github.com/sindresorhus/got'),
linkifyUrls('See https://sindresorhus.com and https://github.com/sindresorhus/got'),
'See <a href="https://sindresorhus.com">https://sindresorhus.com</a> and <a href="https://github.com/sindresorhus/got">https://github.com/sindresorhus/got</a>'
);

t.is(
m('See https://sindresorhus.com', {
linkifyUrls('See https://sindresorhus.com', {
attributes: {
class: 'unicorn',
target: '_blank'
Expand All @@ -44,14 +44,14 @@ test('main', t => {
);

t.is(
m('[![Build Status](https://travis-ci.org/sindresorhus/caprine.svg?branch=master)](https://travis-ci.org/sindresorhus/caprine)'),
linkifyUrls('[![Build Status](https://travis-ci.org/sindresorhus/caprine.svg?branch=master)](https://travis-ci.org/sindresorhus/caprine)'),
'[![Build Status](<a href="https://travis-ci.org/sindresorhus/caprine.svg?branch=master">https://travis-ci.org/sindresorhus/caprine.svg?branch=master</a>)](<a href="https://travis-ci.org/sindresorhus/caprine">https://travis-ci.org/sindresorhus/caprine</a>)'
);
});

test('supports boolean and non-string attribute values', t => {
t.is(
m('https://sindresorhus.com', {
linkifyUrls('https://sindresorhus.com', {
attributes: {
foo: true,
bar: false,
Expand All @@ -64,14 +64,14 @@ test('supports boolean and non-string attribute values', t => {

test('DocumentFragment support', t => {
t.is(
html(m('See https://sindresorhus.com and https://github.com/sindresorhus/got', {
html(linkifyUrls('See https://sindresorhus.com and https://github.com/sindresorhus/got', {
type: 'dom'
})),
html(domify('See <a href="https://sindresorhus.com">https://sindresorhus.com</a> and <a href="https://github.com/sindresorhus/got">https://github.com/sindresorhus/got</a>'))
);

t.is(
html(m('See https://sindresorhus.com', {
html(linkifyUrls('See https://sindresorhus.com', {
type: 'dom',
attributes: {
class: 'unicorn',
Expand All @@ -82,63 +82,63 @@ test('DocumentFragment support', t => {
);

t.is(
html(m('[![Build Status](https://travis-ci.org/sindresorhus/caprine.svg?branch=master)](https://travis-ci.org/sindresorhus/caprine)', {
html(linkifyUrls('[![Build Status](https://travis-ci.org/sindresorhus/caprine.svg?branch=master)](https://travis-ci.org/sindresorhus/caprine)', {
type: 'dom'
})),
html(domify('[![Build Status](<a href="https://travis-ci.org/sindresorhus/caprine.svg?branch=master">https://travis-ci.org/sindresorhus/caprine.svg?branch=master</a>)](<a href="https://travis-ci.org/sindresorhus/caprine">https://travis-ci.org/sindresorhus/caprine</a>)'))
);
});

test('escapes the URL', t => {
t.is(m('http://mysite.com/?emp=1&amp=2'), '<a href="http://mysite.com/?emp=1&amp;amp=2">http://mysite.com/?emp=1&amp;amp=2</a>');
t.is(linkifyUrls('http://mysite.com/?emp=1&amp=2'), '<a href="http://mysite.com/?emp=1&amp;amp=2">http://mysite.com/?emp=1&amp;amp=2</a>');
});

test('supports `@` in the URL path', t => {
t.is(m('https://sindresorhus.com/@foo'), '<a href="https://sindresorhus.com/@foo">https://sindresorhus.com/@foo</a>');
t.is(linkifyUrls('https://sindresorhus.com/@foo'), '<a href="https://sindresorhus.com/@foo">https://sindresorhus.com/@foo</a>');
});

test('supports `#!` in the URL path', t => {
t.is(m('https://twitter.com/#!/sindresorhus'), '<a href="https://twitter.com/#!/sindresorhus">https://twitter.com/#!/sindresorhus</a>');
t.is(linkifyUrls('https://twitter.com/#!/sindresorhus'), '<a href="https://twitter.com/#!/sindresorhus">https://twitter.com/#!/sindresorhus</a>');
});

test('supports `,` in the URL path, but not at the end', t => {
t.is(m('https://sindresorhus.com/?id=foo,bar'), '<a href="https://sindresorhus.com/?id=foo,bar">https://sindresorhus.com/?id=foo,bar</a>');
t.is(m('https://sindresorhus.com/?id=foo, bar'), '<a href="https://sindresorhus.com/?id=foo">https://sindresorhus.com/?id=foo</a>, bar');
t.is(linkifyUrls('https://sindresorhus.com/?id=foo,bar'), '<a href="https://sindresorhus.com/?id=foo,bar">https://sindresorhus.com/?id=foo,bar</a>');
t.is(linkifyUrls('https://sindresorhus.com/?id=foo, bar'), '<a href="https://sindresorhus.com/?id=foo">https://sindresorhus.com/?id=foo</a>, bar');
});

test('supports `value` option', t => {
t.is(m('See https://github.com/sindresorhus.com/linkify-urls for a solution', {
t.is(linkifyUrls('See https://github.com/sindresorhus.com/linkify-urls for a solution', {
type: 'string',
value: 0
}), 'See <a href="https://github.com/sindresorhus.com/linkify-urls">0</a> for a solution');
});

test('supports `value` option as function', t => {
t.is(m('See https://github.com/sindresorhus.com/linkify-urls for a solution', {
t.is(linkifyUrls('See https://github.com/sindresorhus.com/linkify-urls for a solution', {
value: url => new URL(url).hostname
}), 'See <a href="https://github.com/sindresorhus.com/linkify-urls">github.com</a> for a solution');
});

test('skips URLs preceded by a `+` sign', t => {
const fixture = 'git+https://github.com/sindresorhus/ava';
t.is(m(fixture), fixture);
t.is(linkifyUrls(fixture), fixture);
});

test('supports username in url', t => {
t.is(m('https://user@sindresorhus.com/@foo'), '<a href="https://user@sindresorhus.com/@foo">https://user@sindresorhus.com/@foo</a>');
t.is(linkifyUrls('https://user@sindresorhus.com/@foo'), '<a href="https://user@sindresorhus.com/@foo">https://user@sindresorhus.com/@foo</a>');
});

test('supports a URL with a subdomain', t => {
t.is(m('http://docs.google.com'), '<a href="http://docs.google.com">http://docs.google.com</a>');
t.is(linkifyUrls('http://docs.google.com'), '<a href="http://docs.google.com">http://docs.google.com</a>');
});

test('skips email addresses', t => {
t.is(m('sindre@example.com'), 'sindre@example.com');
t.is(m('www.sindre@example.com'), 'www.sindre@example.com');
t.is(m('sindre@www.example.com'), 'sindre@www.example.com');
t.is(linkifyUrls('sindre@example.com'), 'sindre@example.com');
t.is(linkifyUrls('www.sindre@example.com'), 'www.sindre@example.com');
t.is(linkifyUrls('sindre@www.example.com'), 'sindre@www.example.com');
});

test('supports localhost URLs', t => {
t.is(m('http://localhost'), '<a href="http://localhost">http://localhost</a>');
t.is(m('http://localhost/foo/bar'), '<a href="http://localhost/foo/bar">http://localhost/foo/bar</a>');
t.is(linkifyUrls('http://localhost'), '<a href="http://localhost">http://localhost</a>');
t.is(linkifyUrls('http://localhost/foo/bar'), '<a href="http://localhost/foo/bar">http://localhost/foo/bar</a>');
});

0 comments on commit cc77665

Please sign in to comment.