From 2311f30447742b5bac1945b577537fa208c36791 Mon Sep 17 00:00:00 2001 From: holmofy Date: Sun, 23 May 2021 16:31:13 +0800 Subject: [PATCH 01/11] add glob support --- index.js | 2 + lib/filter.js | 100 +++++++++++++++++++++++++++++++++++++------------- package.json | 3 ++ test/index.js | 36 ++++++++++++++---- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index 9f05fd5..d086900 100755 --- a/index.js +++ b/index.js @@ -5,6 +5,8 @@ hexo.config.nofollow = Object.assign({ enable: true, field: 'site', + elements: ['a'], + include: [], exclude: [] }, hexo.config.nofollow); diff --git a/lib/filter.js b/lib/filter.js index ba020b9..c083cf9 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -1,56 +1,102 @@ 'use strict'; const { parse } = require('url'); +const { Minimatch, filter } = require('minimatch'); +/** + * Check whether the url is an external link + * @param {string} url + * @param {object} config + * @returns boolean + */ function isExternal(url, config) { - const exclude = config.nofollow.exclude; + const { includeGlobs, excludeGlobs } = config.nofollow; const data = parse(url); - const host = data.hostname; + const { hostname, path } = data; const sitehost = parse(config.url).hostname || config.url; - if (!data.protocol || !sitehost) return false; + if (!data.protocol || !hostname || !sitehost) return false; - if (exclude && exclude.length) { - for (const i of exclude) { - if (host === i) return false; + const target = hostname + path; + + if (excludeGlobs && excludeGlobs.length) { + for (const glob of excludeGlobs) { + if (glob.match(target)) return false; } } - if (host !== sitehost) return true; + if (includeGlobs && includeGlobs.length) { + for (const glob of includeGlobs) { + if (glob.match(target)) return true; + } + } - return false; + return hostname !== sitehost; } -module.exports = function(data) { +/** + * Add the nofollow attribute to the tag + * @param {string} tagStr + * @param {string} urlAttrStr + * @returns new tag string + */ +function nofollow(tagStr, urlAttrStr) { + let noFollow = ['noopener', 'external', 'nofollow', 'noreferrer']; + + if (/rel=/gi.test(tagStr)) { + tagStr = tagStr.replace(/\srel="(.*?)"/gi, (relStr, rel) => { + rel = rel.split(' '); + noFollow.push(...rel); + // De-duplicate + noFollow = [...new Set(noFollow)]; + + return ''; + }); + } + return tagStr.replace(urlAttrStr, `${urlAttrStr} rel="${noFollow.join(' ')}"`); +} + +module.exports = function (data) { const hexo = this; const config = hexo.config; - const exclude = config.nofollow.exclude; + const { elements, include, exclude, minimatch } = config.nofollow; + if (elements && !Array.isArray(elements)) { + config.nofollow.elements = [elements]; + } + if (include && !Array.isArray(include)) { + config.nofollow.include = [include]; + } if (exclude && !Array.isArray(exclude)) { config.nofollow.exclude = [exclude]; } - const filterExternal = data => { - return data.replace(//gi, (str, hrefStr, href) => { - if (!isExternal(href, config)) return str; - - let noFollow = ['noopener', 'external', 'nofollow', 'noreferrer']; - - if (/rel=/gi.test(str)) { - str = str.replace(/\srel="(.*?)"/gi, (relStr, rel) => { - rel = rel.split(' '); - noFollow.push(...rel); - // De-duplicate - noFollow = [...new Set(noFollow)]; + config.nofollow.includeGlobs = config.nofollow.include.map(pattern => new Minimatch(pattern, minimatch)); + config.nofollow.excludeGlobs = config.nofollow.exclude.map(pattern => new Minimatch(pattern, minimatch)); - return ''; - }); - } + const filterATagHrefExternal = data => { + return data.replace(//gi, (aTagSrc, hrefAttrStr, href) => { + if (!isExternal(href, config)) return aTagSrc; + return nofollow(aTagSrc, hrefAttrStr); + }); + }; - return str.replace(hrefStr, `${hrefStr} rel="${noFollow.join(' ')}"`); + const filterImgTagSrcExternal = data => { + return data.replace(//gi, (imgTagSrc, srcAttrStr, src) => { + if (!isExternal(src, config)) return imgTagSrc; + return nofollow(imgTagSrc, srcAttrStr); }); }; + const filterExternal = data => { + if (config.nofollow.elements.includes('a')) + data = filterATagHrefExternal(data); + if (config.nofollow.elements.includes('img')) { + data = filterImgTagSrcExternal(data); + } + return data; + } + if (config.nofollow.field === 'post') { data.content = filterExternal(data.content); } else { @@ -59,3 +105,5 @@ module.exports = function(data) { return data; }; + + diff --git a/package.json b/package.json index 01213a4..4a47e24 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,8 @@ "eslint-config-hexo": "^4.1.0", "hexo": "hexojs/hexo", "mocha": "^8.0.1" + }, + "dependencies": { + "minimatch": "^3.0.4" } } diff --git a/test/index.js b/test/index.js index 161b609..93a52a0 100755 --- a/test/index.js +++ b/test/index.js @@ -9,49 +9,71 @@ describe('hexo-filter-nofollow', () => { const nofollowFilter = require('../lib/filter').bind(hexo); hexo.config.url = 'https://example.com'; - hexo.config.nofollow = {}; + hexo.config.nofollow = { include: [], exclude: [], elements: ['a', 'img'] }; describe('Default', () => { const content = [ '# External link test', '1. External link', 'Hexo', + 'Hexo', '2. External link with existed "rel" Attribute', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '3. External link with existing "rel=noopener", "rel=external" or "rel=noreferrer"', 'Hexo', 'Hexo', 'Hexo', 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '4. External link with Other Attributes', 'Hexo', 'Hexo', + 'Hexo', + 'Hexo', '5. Internal link', 'Link', + 'Link', '6. Ignore links don\'t have "href" attribute', - 'Anchor' + 'Anchor', + 'Anchor' ].join('\n'); const expected = [ '# External link test', '1. External link', 'Hexo', + 'Hexo', '2. External link with existed "rel" Attribute', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '3. External link with existing "rel=noopener", "rel=external" or "rel=noreferrer"', 'Hexo', 'Hexo', 'Hexo', 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '4. External link with Other Attributes', 'Hexo', 'Hexo', + 'Hexo', + 'Hexo', '5. Internal link', 'Link', + 'Link', '6. Ignore links don\'t have "href" attribute', - 'Anchor' + 'Anchor', + 'Anchor' ].join('\n'); it('Default to field = "site"', () => { From 629207175b154fde85d1bb54954e96eba6f99aa8 Mon Sep 17 00:00:00 2001 From: holmofy Date: Sun, 23 May 2021 18:34:25 +0800 Subject: [PATCH 02/11] update referrepolicy --- lib/filter.js | 47 ++++++++++++++++++++++++----------------------- test/index.js | 42 +++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/lib/filter.js b/lib/filter.js index c083cf9..b6c6c3b 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -35,25 +35,30 @@ function isExternal(url, config) { } /** - * Add the nofollow attribute to the tag + * Add attribute to the tag * @param {string} tagStr * @param {string} urlAttrStr + * @param {string} attrKey + * @param {string | array} attrValue * @returns new tag string */ -function nofollow(tagStr, urlAttrStr) { - let noFollow = ['noopener', 'external', 'nofollow', 'noreferrer']; - - if (/rel=/gi.test(tagStr)) { - tagStr = tagStr.replace(/\srel="(.*?)"/gi, (relStr, rel) => { - rel = rel.split(' '); - noFollow.push(...rel); - // De-duplicate - noFollow = [...new Set(noFollow)]; - +function addAttr(tagStr, urlAttrStr, attrKey, attrValue) { + const value = toArray(attrValue); + const regexKey = new RegExp(`${attrKey}=`, 'gi'); + const attrRegex = new RegExp(`\\s${attrKey}="(.*?)"`, 'gi'); + if (regexKey.test(tagStr)) { + tagStr = tagStr.replace(attrRegex, (attrStr, attrStrValue) => { + value.push(...attrStrValue.split(' ')); return ''; }); } - return tagStr.replace(urlAttrStr, `${urlAttrStr} rel="${noFollow.join(' ')}"`); + // De-duplicate + const uniqValue = [...new Set(value)]; + return tagStr.replace(urlAttrStr, `${urlAttrStr} ${attrKey}="${uniqValue.join(' ')}"`); +} + +function toArray(data) { + return data && !Array.isArray(data) ? [data] : data; } module.exports = function (data) { @@ -61,15 +66,9 @@ module.exports = function (data) { const config = hexo.config; const { elements, include, exclude, minimatch } = config.nofollow; - if (elements && !Array.isArray(elements)) { - config.nofollow.elements = [elements]; - } - if (include && !Array.isArray(include)) { - config.nofollow.include = [include]; - } - if (exclude && !Array.isArray(exclude)) { - config.nofollow.exclude = [exclude]; - } + config.nofollow.elements = toArray(elements); + config.nofollow.include = toArray(include); + config.nofollow.exclude = toArray(exclude); config.nofollow.includeGlobs = config.nofollow.include.map(pattern => new Minimatch(pattern, minimatch)); config.nofollow.excludeGlobs = config.nofollow.exclude.map(pattern => new Minimatch(pattern, minimatch)); @@ -77,14 +76,16 @@ module.exports = function (data) { const filterATagHrefExternal = data => { return data.replace(//gi, (aTagSrc, hrefAttrStr, href) => { if (!isExternal(href, config)) return aTagSrc; - return nofollow(aTagSrc, hrefAttrStr); + aTagSrc = addAttr(aTagSrc, hrefAttrStr, 'referrerpolicy', 'no-referrer'); + return addAttr(aTagSrc, hrefAttrStr, 'rel', ['noopener', 'external', 'nofollow', 'noreferrer']); }); }; const filterImgTagSrcExternal = data => { return data.replace(//gi, (imgTagSrc, srcAttrStr, src) => { if (!isExternal(src, config)) return imgTagSrc; - return nofollow(imgTagSrc, srcAttrStr); + imgTagSrc = addAttr(imgTagSrc, srcAttrStr, 'referrerpolicy', 'no-referrer'); + return addAttr(imgTagSrc, srcAttrStr, 'rel', ['noopener', 'external', 'nofollow', 'noreferrer']); }); }; diff --git a/test/index.js b/test/index.js index 93a52a0..372a8f2 100755 --- a/test/index.js +++ b/test/index.js @@ -47,27 +47,27 @@ describe('hexo-filter-nofollow', () => { const expected = [ '# External link test', '1. External link', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', '2. External link with existed "rel" Attribute', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '3. External link with existing "rel=noopener", "rel=external" or "rel=noreferrer"', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '4. External link with Other Attributes', - 'Hexo', - 'Hexo', - 'Hexo', - 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', + 'Hexo', '5. Internal link', 'Link', 'Link', @@ -114,12 +114,12 @@ describe('hexo-filter-nofollow', () => { result.should.eql([ '# Exclude link test', '1. External link', - 'Hexo', + 'Hexo', '2. Ignore links whose hostname is same as config', 'Example Domain', '3. Ignore links whose hostname is included in exclude', 'Example Domain', - 'Example Domain' + 'Example Domain' ].join('\n')); }); @@ -131,7 +131,7 @@ describe('hexo-filter-nofollow', () => { result.should.eql([ '# Exclude link test', '1. External link', - 'Hexo', + 'Hexo', '2. Ignore links whose hostname is same as config', 'Example Domain', '3. Ignore links whose hostname is included in exclude', From 5939040226e3cb07a21d97cf100a2e3d13e5059d Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:00:37 +0800 Subject: [PATCH 03/11] fix comment --- lib/filter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/filter.js b/lib/filter.js index b6c6c3b..4f508b3 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -36,10 +36,10 @@ function isExternal(url, config) { /** * Add attribute to the tag - * @param {string} tagStr - * @param {string} urlAttrStr - * @param {string} attrKey - * @param {string | array} attrValue + * @param {string} source tag string + * @param {string} attribute string containing the url + * @param {string} new attribute key + * @param {string | array} new attribute value * @returns new tag string */ function addAttr(tagStr, urlAttrStr, attrKey, attrValue) { From ae2df5bbac313adbc57f8a2c850456bd104e7cca Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:18:19 +0800 Subject: [PATCH 04/11] add readme doc --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f850823..ba1ab34 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ $ npm i hexo-filter-nofollow --save nofollow: enable: true field: site + elements: + - 'a' + - 'img' exclude: - 'exclude1.com' - 'exclude2.com' @@ -30,5 +33,6 @@ nofollow: - **field** - The scope you want the plugin to proceed, can be 'site' or 'post'. Default value is `site`. - 'post' - Only add nofollow attribute to external links in your post content - 'site' - Add nofollow attribute to external links of whole sites +- **elements** - The tag to be processed, currently only supports `` and ``. - **exclude** - Exclude hostname. Specify subdomain when applicable, including `www`. - 'exclude1.com' does not apply to `www.exclude1.com` nor `en.exclude1.com`. From c51a2efcd4936aed7c96392f7ffe147098588585 Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:26:28 +0800 Subject: [PATCH 05/11] add readme doc --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ba1ab34..686b57c 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ nofollow: - 'a' - 'img' exclude: - - 'exclude1.com' - - 'exclude2.com' + - '*.exclude1.com' + - 'exclude2.com/path/*' ``` - **enable** - Enable the plugin. Default value is `true`. @@ -34,5 +34,8 @@ nofollow: - 'post' - Only add nofollow attribute to external links in your post content - 'site' - Add nofollow attribute to external links of whole sites - **elements** - The tag to be processed, currently only supports `` and ``. -- **exclude** - Exclude hostname. Specify subdomain when applicable, including `www`. - - 'exclude1.com' does not apply to `www.exclude1.com` nor `en.exclude1.com`. +- **include** - Include hostname. You can use `*` or `?` glob wildcards. +- **exclude** - Exclude hostname. You can use `*` or `?` glob wildcards. + - `exclude1.com` does not apply to `www.exclude1.com` nor `en.exclude1.com`. + - `*.exclude1.com` can be apply to `www.exclude1.com` or `en.exclude1.com`. +- **minimatch** - The glob wildcard is supported by [minimath](https://github.com/isaacs/minimatch), this field can be configured for it. From 96125c7435bd75991b00b26143e4bac9f204efd6 Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:34:48 +0800 Subject: [PATCH 06/11] configurable noreferrer --- index.js | 4 +++- lib/filter.js | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index d086900..ea34bba 100755 --- a/index.js +++ b/index.js @@ -7,7 +7,9 @@ hexo.config.nofollow = Object.assign({ field: 'site', elements: ['a'], include: [], - exclude: [] + exclude: [], + rel: ['noopener', 'external', 'nofollow', 'noreferrer'], + referrerpolicy: 'no-referrer' }, hexo.config.nofollow); const config = hexo.config.nofollow; diff --git a/lib/filter.js b/lib/filter.js index 4f508b3..d82d576 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -76,16 +76,16 @@ module.exports = function (data) { const filterATagHrefExternal = data => { return data.replace(//gi, (aTagSrc, hrefAttrStr, href) => { if (!isExternal(href, config)) return aTagSrc; - aTagSrc = addAttr(aTagSrc, hrefAttrStr, 'referrerpolicy', 'no-referrer'); - return addAttr(aTagSrc, hrefAttrStr, 'rel', ['noopener', 'external', 'nofollow', 'noreferrer']); + aTagSrc = addAttr(aTagSrc, hrefAttrStr, 'referrerpolicy', config.nofollow.referrerpolicy); + return addAttr(aTagSrc, hrefAttrStr, 'rel', config.nofollow.rel); }); }; const filterImgTagSrcExternal = data => { return data.replace(//gi, (imgTagSrc, srcAttrStr, src) => { if (!isExternal(src, config)) return imgTagSrc; - imgTagSrc = addAttr(imgTagSrc, srcAttrStr, 'referrerpolicy', 'no-referrer'); - return addAttr(imgTagSrc, srcAttrStr, 'rel', ['noopener', 'external', 'nofollow', 'noreferrer']); + imgTagSrc = addAttr(imgTagSrc, srcAttrStr, 'referrerpolicy', config.nofollow.referrerpolicy); + return addAttr(imgTagSrc, srcAttrStr, 'rel', config.nofollow.rel); }); }; From 514cf0d6a1ed1fcc4ef3be0d07ab245e088d2071 Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:49:31 +0800 Subject: [PATCH 07/11] fix test bug --- lib/filter.js | 2 +- test/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/filter.js b/lib/filter.js index d82d576..f5eb905 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -43,7 +43,7 @@ function isExternal(url, config) { * @returns new tag string */ function addAttr(tagStr, urlAttrStr, attrKey, attrValue) { - const value = toArray(attrValue); + const value = [...toArray(attrValue)]; const regexKey = new RegExp(`${attrKey}=`, 'gi'); const attrRegex = new RegExp(`\\s${attrKey}="(.*?)"`, 'gi'); if (regexKey.test(tagStr)) { diff --git a/test/index.js b/test/index.js index 372a8f2..b54f0b1 100755 --- a/test/index.js +++ b/test/index.js @@ -9,7 +9,7 @@ describe('hexo-filter-nofollow', () => { const nofollowFilter = require('../lib/filter').bind(hexo); hexo.config.url = 'https://example.com'; - hexo.config.nofollow = { include: [], exclude: [], elements: ['a', 'img'] }; + hexo.config.nofollow = { include: [], exclude: [], elements: ['a', 'img'], rel: ['noopener', 'external', 'nofollow', 'noreferrer'], referrerpolicy: 'no-referrer' }; describe('Default', () => { const content = [ From 98294e571dff2a4cb6656712c88daa6fbce7ca3c Mon Sep 17 00:00:00 2001 From: holmofy Date: Fri, 28 May 2021 17:55:36 +0800 Subject: [PATCH 08/11] update readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 686b57c..9f84eb6 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,12 @@ nofollow: exclude: - '*.exclude1.com' - 'exclude2.com/path/*' + rel: + - 'external' + - 'noreferrer' + - 'nofollow' + - 'noopener' + referrerpolicy: 'no-referrer' ``` - **enable** - Enable the plugin. Default value is `true`. @@ -39,3 +45,5 @@ nofollow: - `exclude1.com` does not apply to `www.exclude1.com` nor `en.exclude1.com`. - `*.exclude1.com` can be apply to `www.exclude1.com` or `en.exclude1.com`. - **minimatch** - The glob wildcard is supported by [minimath](https://github.com/isaacs/minimatch), this field can be configured for it. +- **rel** - Configurable rel attribute value. +- **referrerpolicy** - Configurable referrerpolicy attribute value. From 871b6400e6800e45684bdda7b4c4eaffa22cf01f Mon Sep 17 00:00:00 2001 From: holmofy Date: Mon, 31 May 2021 15:45:46 +0800 Subject: [PATCH 09/11] add testcase and fix bug --- lib/filter.js | 6 +++++- test/index.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/filter.js b/lib/filter.js index f5eb905..dea1cb8 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -31,7 +31,11 @@ function isExternal(url, config) { } } - return hostname !== sitehost; + if (includeGlobs && includeGlobs.length) { + // If include is configured, other links will not be treated as external links + return false; + } + return hostname != sitehost; } /** diff --git a/test/index.js b/test/index.js index b54f0b1..983d0ab 100755 --- a/test/index.js +++ b/test/index.js @@ -94,6 +94,55 @@ describe('hexo-filter-nofollow', () => { }); }); + describe('Include & Pattern', () => { + const content = [ + '# Include & Pattern link test', + '1. External link', + 'Hexo', + '2. Ignore links whose hostname is not match glob pattern', + 'Example Domain', + '3. Ignore links whose hostname is included in glob pattern', + 'Example Domain', + 'Example Domain' + ].join('\n'); + + it('String', () => { + hexo.config.nofollow.include = ['hexo.io', '*.example.org']; + + const result = nofollowFilter(content); + + result.should.eql([ + '# Include & Pattern link test', + '1. External link', + 'Hexo', + '2. Ignore links whose hostname is not match glob pattern', + 'Example Domain', + '3. Ignore links whose hostname is included in glob pattern', + 'Example Domain', + 'Example Domain' + ].join('\n')); + }); + + it('Array', () => { + hexo.config.nofollow.include = 'hexo.io'; + hexo.config.nofollow.exclude = ['example.org', '*.example.org']; + + const result = nofollowFilter(content); + + result.should.eql([ + '# Include & Pattern link test', + '1. External link', + 'Hexo', + '2. Ignore links whose hostname is not match glob pattern', + 'Example Domain', + '3. Ignore links whose hostname is included in glob pattern', + 'Example Domain', + 'Example Domain' + ].join('\n')); + }); + }); + + describe('Exclude', () => { const content = [ '# Exclude link test', @@ -107,6 +156,7 @@ describe('hexo-filter-nofollow', () => { ].join('\n'); it('String', () => { + hexo.config.nofollow.include = []; hexo.config.nofollow.exclude = 'example.org'; const result = nofollowFilter(content); From 9007efcda3dfad2496aab6aab37efd1db08cfe01 Mon Sep 17 00:00:00 2001 From: holmofy Date: Mon, 31 May 2021 15:47:30 +0800 Subject: [PATCH 10/11] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f84eb6..66ff11e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ nofollow: - 'post' - Only add nofollow attribute to external links in your post content - 'site' - Add nofollow attribute to external links of whole sites - **elements** - The tag to be processed, currently only supports `` and ``. -- **include** - Include hostname. You can use `*` or `?` glob wildcards. +- **include** - Include hostname. You can use `*` or `?` glob wildcards. If include is configured, other external links will not be processed. - **exclude** - Exclude hostname. You can use `*` or `?` glob wildcards. - `exclude1.com` does not apply to `www.exclude1.com` nor `en.exclude1.com`. - `*.exclude1.com` can be apply to `www.exclude1.com` or `en.exclude1.com`. From c5c0a79d5647dc24fda95100f1fb5ff6c88e8856 Mon Sep 17 00:00:00 2001 From: holmofy Date: Sat, 12 Jun 2021 16:53:04 +0800 Subject: [PATCH 11/11] fix bug --- lib/filter.js | 41 +++++++++++++++++++++++++---------------- test/index.js | 29 +++++++++++++++++++---------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/lib/filter.js b/lib/filter.js index dea1cb8..846c501 100755 --- a/lib/filter.js +++ b/lib/filter.js @@ -1,12 +1,12 @@ 'use strict'; const { parse } = require('url'); -const { Minimatch, filter } = require('minimatch'); +const { Minimatch } = require('minimatch'); /** * Check whether the url is an external link - * @param {string} url - * @param {object} config + * @param {string} url + * @param {object} config * @returns boolean */ function isExternal(url, config) { @@ -65,7 +65,15 @@ function toArray(data) { return data && !Array.isArray(data) ? [data] : data; } -module.exports = function (data) { +function addSitePath(sitePattern) { + if (!sitePattern || sitePattern.indexOf('/') >= 0) { + return sitePattern; + } + // default wildcard under the site + return sitePattern + '/**'; +} + +module.exports = function nofollow(data) { const hexo = this; const config = hexo.config; @@ -74,33 +82,34 @@ module.exports = function (data) { config.nofollow.include = toArray(include); config.nofollow.exclude = toArray(exclude); - config.nofollow.includeGlobs = config.nofollow.include.map(pattern => new Minimatch(pattern, minimatch)); - config.nofollow.excludeGlobs = config.nofollow.exclude.map(pattern => new Minimatch(pattern, minimatch)); + config.nofollow.includeGlobs = config.nofollow.include.map(pattern => new Minimatch(addSitePath(pattern), minimatch)); + config.nofollow.excludeGlobs = config.nofollow.exclude.map(pattern => new Minimatch(addSitePath(pattern), minimatch)); const filterATagHrefExternal = data => { - return data.replace(//gi, (aTagSrc, hrefAttrStr, href) => { - if (!isExternal(href, config)) return aTagSrc; - aTagSrc = addAttr(aTagSrc, hrefAttrStr, 'referrerpolicy', config.nofollow.referrerpolicy); - return addAttr(aTagSrc, hrefAttrStr, 'rel', config.nofollow.rel); + return data.replace(//gi, (aTagRaw, hrefAttrRaw, href) => { + if (!isExternal(href, config)) return aTagRaw; + aTagRaw = addAttr(aTagRaw, hrefAttrRaw, 'referrerpolicy', config.nofollow.referrerpolicy); + return addAttr(aTagRaw, hrefAttrRaw, 'rel', config.nofollow.rel); }); }; const filterImgTagSrcExternal = data => { - return data.replace(//gi, (imgTagSrc, srcAttrStr, src) => { - if (!isExternal(src, config)) return imgTagSrc; - imgTagSrc = addAttr(imgTagSrc, srcAttrStr, 'referrerpolicy', config.nofollow.referrerpolicy); - return addAttr(imgTagSrc, srcAttrStr, 'rel', config.nofollow.rel); + return data.replace(//gi, (imgTagRaw, srcAttrRaw, src) => { + if (!isExternal(src, config)) return imgTagRaw; + imgTagRaw = addAttr(imgTagRaw, srcAttrRaw, 'referrerpolicy', config.nofollow.referrerpolicy); + return addAttr(imgTagRaw, srcAttrRaw, 'rel', config.nofollow.rel); }); }; const filterExternal = data => { - if (config.nofollow.elements.includes('a')) + if (config.nofollow.elements.includes('a')) { data = filterATagHrefExternal(data); + } if (config.nofollow.elements.includes('img')) { data = filterImgTagSrcExternal(data); } return data; - } + }; if (config.nofollow.field === 'post') { data.content = filterExternal(data.content); diff --git a/test/index.js b/test/index.js index 983d0ab..3fb43fe 100755 --- a/test/index.js +++ b/test/index.js @@ -99,11 +99,14 @@ describe('hexo-filter-nofollow', () => { '# Include & Pattern link test', '1. External link', 'Hexo', - '2. Ignore links whose hostname is not match glob pattern', + '2. External links whose hostname is not match glob pattern', 'Example Domain', - '3. Ignore links whose hostname is included in glob pattern', + '3. External links whose hostname is included in glob pattern', 'Example Domain', - 'Example Domain' + 'Example Domain', + '4. External links whose hostname is included in glob pattern with path', + 'Example Domain', + 'Example Domain' ].join('\n'); it('String', () => { @@ -115,17 +118,20 @@ describe('hexo-filter-nofollow', () => { '# Include & Pattern link test', '1. External link', 'Hexo', - '2. Ignore links whose hostname is not match glob pattern', + '2. External links whose hostname is not match glob pattern', 'Example Domain', - '3. Ignore links whose hostname is included in glob pattern', + '3. External links whose hostname is included in glob pattern', 'Example Domain', - 'Example Domain' + 'Example Domain', + '4. External links whose hostname is included in glob pattern with path', + 'Example Domain', + 'Example Domain' ].join('\n')); }); it('Array', () => { hexo.config.nofollow.include = 'hexo.io'; - hexo.config.nofollow.exclude = ['example.org', '*.example.org']; + hexo.config.nofollow.exclude = ['example.org', '*.example.org', 'path.example.org/**']; const result = nofollowFilter(content); @@ -133,11 +139,14 @@ describe('hexo-filter-nofollow', () => { '# Include & Pattern link test', '1. External link', 'Hexo', - '2. Ignore links whose hostname is not match glob pattern', + '2. External links whose hostname is not match glob pattern', 'Example Domain', - '3. Ignore links whose hostname is included in glob pattern', + '3. External links whose hostname is included in glob pattern', 'Example Domain', - 'Example Domain' + 'Example Domain', + '4. External links whose hostname is included in glob pattern with path', + 'Example Domain', + 'Example Domain' ].join('\n')); }); });