diff --git a/.gitignore b/.gitignore index 9ad1cf3..a03ee43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules npm-debug.* test/coverage +.nyc_output diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..319e41e --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +strict-peer-dependencies=false diff --git a/.travis.yml b/.travis.yml index 3012f98..8c274c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "4" - - "6" + - "12" + - "16" script: "npm run test-travis" diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..5153415 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,2 @@ +declare function isLinkWorking(link: string): Promise +export = isLinkWorking diff --git a/index.js b/index.js index 9468378..9565ac9 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,7 @@ function isOnlineAsPromise() { isOnlineCache = new Promise((resolve, reject) => { isOnline((err, online) => { if (err) { - delete isOnlineCache.promise; // Do not cache errors + delete isOnlineCache.promise; // Do not cache errors /* istanbul ignore next */ reject(err); } else { @@ -32,14 +32,14 @@ function isOnlineAsPromise() { function checkConnectivity(requestErr) { return isOnlineAsPromise() - .catch(() => { throw requestErr; }) - .then((online) => { - if (!online) { - throw requestErr; - } + .catch(() => { throw requestErr; }) + .then((online) => { + if (!online) { + throw requestErr; + } - return false; - }); + return false; + }); } class DevNull extends WritableStream { @@ -50,7 +50,7 @@ class DevNull extends WritableStream { function tryHead(link, gotOptions) { return got.head(link, gotOptions) - .then(() => true); + .then(() => true); } function tryGet(link, options, gotOptions) { @@ -65,29 +65,29 @@ function tryGet(link, options, gotOptions) { } stream - .on('request', (req_) => { req = req_; }) - .on('response', (res) => { - res.on('error', () => {}); // Swallow any response errors, because we are going to abort the request - setImmediate(() => req.abort()); - resolve(true); - }) - .on('error', (err, body, res) => { - res && res.on('error', () => {}); // Swallow any response errors, because we are going to abort the request - setImmediate(() => req.abort()); - - if (err instanceof got.MaxRedirectsError || err instanceof got.HTTPError || err instanceof got.UnsupportedProtocolError) { - return resolve(false); - } + .on('request', (req_) => { req = req_; }) + .on('response', (res) => { + res.on('error', () => {}); // Swallow any response errors, because we are going to abort the request + setImmediate(() => req.abort()); + resolve(true); + }) + .on('error', (err, body, res) => { + res && res.on('error', () => {}); // Swallow any response errors, because we are going to abort the request + setImmediate(() => req.abort()); + + if (err instanceof got.MaxRedirectsError || err instanceof got.HTTPError || err instanceof got.UnsupportedProtocolError) { + return resolve(false); + } + + /* istanbul ignore else */ + if (err instanceof got.RequestError) { + return resolve(options.checkConnectivity ? checkConnectivity(err) : false); + } - /* istanbul ignore else */ - if (err instanceof got.RequestError) { - return resolve(options.checkConnectivity ? checkConnectivity(err) : false); - } - - /* istanbul ignore next */ - reject(err); - }) - .pipe(new DevNull()); + /* istanbul ignore next */ + reject(err); + }) + .pipe(new DevNull()); }); } @@ -113,7 +113,7 @@ function isLinkWorking(link, options) { }; return tryHead(link, gotOptions) - .catch(() => tryGet(link, options, gotOptions)); + .catch(() => tryGet(link, options, gotOptions)); } module.exports = isLinkWorking; diff --git a/package.json b/package.json index 3a9a23d..972b8e9 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,12 @@ "description": "Checks if a given link is working or broken", "main": "index.js", "scripts": { - "lint": "eslint '{*.js,test/**/*.js}' --ignore-pattern=test/coverage", - "test": "mocha --bail", - "test-cov": "istanbul cover --dir test/coverage _mocha -- --bail && echo Coverage lies in test/coverage/lcov-report/index.html", - "test-travis": "istanbul cover _mocha --report lcovonly -- --bail && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" + "lint": "eslint *.js test/**/*.js --ignore-pattern=test/coverage", + "test": "mocha --timeout 10000 --bail", + "test-cov": "nyc --dir test/coverage _mocha -- --timeout 10000 --bail && echo Coverage lies in test/coverage/lcov-report/index.html", + "test-travis": "nyc _mocha --report lcovonly -- --timeout 10000 --bail && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" }, + "types": "./index.d.ts", "bugs": { "url": "https://github.com/IndigoUnited/node-is-link-working/issues/" }, @@ -29,18 +30,18 @@ "author": "IndigoUnited (http://indigounited.com)", "license": "MIT", "dependencies": { - "got": "^7.0.0", - "is-online": "^7.0.0" + "got": "^11.8.5", + "is-online": "^9.0.1" }, "devDependencies": { - "@satazor/eslint-config": "^3.0.0", - "chai": "^4.0.0", - "coveralls": "^2.11.6", - "eslint": "^3.0.0", - "istanbul": "^0.4.1", - "mocha": "^3.0.2", - "mock-require": "^2.0.0", - "nock": "^9.0.0", + "@satazor/eslint-config": "^3.2.0", + "chai": "^4.3.6", + "coveralls": "^3.1.1", + "eslint": "^8.21.0", + "mocha": "^10.0.0", + "mock-require": "^3.0.3", + "nock": "^13.2.9", + "nyc": "^15.1.0", "promise-delay": "^2.1.0" }, "engines": { diff --git a/test/test.js b/test/test.js index bab7622..e90df45 100644 --- a/test/test.js +++ b/test/test.js @@ -14,30 +14,30 @@ afterEach(() => mockRequire.stopAll()); it('should resolve to true for http://google.com (HEAD)', () => { return isLinkWorking('http://google.com') - .then((working) => expect(working).to.equal(true)); + .then((working) => expect(working).to.equal(true)); }); it('should resolve to true for http://google.com (GET)', () => { const nocked = nock('http://google.com') - .head('/') - .reply(404, () => nock.cleanAll()); + .head('/') + .reply(404, () => nock.cleanAll()); return isLinkWorking('http://google.com') - .then((working) => expect(working).to.equal(true)) - .then(() => nocked.done(), (err) => { - nock.cleanAll(); - throw err; - }); + .then((working) => expect(working).to.equal(true)) + .then(() => nocked.done(), (err) => { + nock.cleanAll(); + throw err; + }); }); it('should resolve to false for http://thisdomainwillneverexist.org (domain not found)', () => { return isLinkWorking('http://thisdomainwillneverexist.org') - .then((working) => expect(working).to.equal(false)); + .then((working) => expect(working).to.equal(false)); }); it('should resolve to false for https://github.com/somepagethatwillneverexist (404)', () => { return isLinkWorking('https://github.com/somepagethatwillneverexist') - .then((working) => expect(working).to.equal(false)); + .then((working) => expect(working).to.equal(false)); }); it('should pass the correct options to `got`', () => { @@ -57,43 +57,43 @@ it('should pass the correct options to `got`', () => { const isLinkWorking = mockRequire.reRequire('../'); return Promise.resolve() - .then(() => { - return isLinkWorking('http://google.com') .then(() => { - expect(options).to.eql({ - followRedirect: true, - retries: 3, - timeout: 15000, - agent: null, - headers: { - 'user-agent': `is-link-working/${pkg.version} (https://github.com/IndigoUnited/is-link-working)`, - }, - }); - }); - }) - .then(() => { - return isLinkWorking('http://somepagethatwillneverexist.org', { - followRedirect: false, - retries: 1, - timeout: 5000, + return isLinkWorking('http://google.com') + .then(() => { + expect(options).to.eql({ + followRedirect: true, + retries: 3, + timeout: 15000, + agent: null, + headers: { + 'user-agent': `is-link-working/${pkg.version} (https://github.com/IndigoUnited/is-link-working)`, + }, + }); + }); }) .then(() => { - expect(options).to.eql({ + return isLinkWorking('http://somepagethatwillneverexist.org', { followRedirect: false, retries: 1, timeout: 5000, - agent: null, - headers: { - 'user-agent': `is-link-working/${pkg.version} (https://github.com/IndigoUnited/is-link-working)`, - }, - }); + }) + .then(() => { + expect(options).to.eql({ + followRedirect: false, + retries: 1, + timeout: 5000, + agent: null, + headers: { + 'user-agent': `is-link-working/${pkg.version} (https://github.com/IndigoUnited/is-link-working)`, + }, + }); + }); }); - }); }); it('should report broken with urls having wrong protocols', () => { return isLinkWorking('htttps://google.com') - .then((working) => expect(working).to.equal(false)); + .then((working) => expect(working).to.equal(false)); }); describe('connectivity check', () => { @@ -108,10 +108,10 @@ describe('connectivity check', () => { const isLinkWorking = mockRequire.reRequire('../'); return isLinkWorking('http://thisdomainwillneverexist.org') - .then((working) => { - expect(called).to.equal(false); - expect(working).to.equal(false); - }); + .then((working) => { + expect(called).to.equal(false); + expect(working).to.equal(false); + }); }); it('should resolve to false if request failed but we are online', () => { @@ -125,10 +125,10 @@ describe('connectivity check', () => { const isLinkWorking = mockRequire.reRequire('../'); return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then((working) => { - expect(called).to.equal(true); - expect(working).to.equal(false); - }); + .then((working) => { + expect(called).to.equal(true); + expect(working).to.equal(false); + }); }); it('should reject if request failed and we are NOT online', () => { @@ -142,12 +142,12 @@ describe('connectivity check', () => { const isLinkWorking = mockRequire.reRequire('../'); return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then(() => { - throw new Error('Should have failed'); - }, (err) => { - expect(called).to.equal(true); - expect(err).to.be.an.instanceOf(got.RequestError); - }); + .then(() => { + throw new Error('Should have failed'); + }, (err) => { + expect(called).to.equal(true); + expect(err).to.be.an.instanceOf(got.RequestError); + }); }); it('should handle connectivity check errors, rejecting with the original request error', () => { @@ -161,12 +161,12 @@ describe('connectivity check', () => { const isLinkWorking = mockRequire.reRequire('../'); return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then(() => { - throw new Error('Should have failed'); - }, (err) => { - expect(called).to.equal(true); - expect(err).to.be.an.instanceOf(got.RequestError); - }); + .then(() => { + throw new Error('Should have failed'); + }, (err) => { + expect(called).to.equal(true); + expect(err).to.be.an.instanceOf(got.RequestError); + }); }); }); @@ -184,24 +184,24 @@ describe('connectivity cache', () => { isLinkWorking.connectivityCacheDuration = 100; return Promise.resolve() - .then(() => { - return Promise.all([ - isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), - promiseDelay(50).then(() => isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true })), - ]) - .then((working) => { - expect(working).to.eql([false, false]); - expect(nrCalls).to.equal(1); - }); - }) - .then(() => promiseDelay(75)) - .then(() => { - return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then((working) => { - expect(working).to.equal(false); - expect(nrCalls).to.equal(2); + .then(() => { + return Promise.all([ + isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), + promiseDelay(50).then(() => isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true })), + ]) + .then((working) => { + expect(working).to.eql([false, false]); + expect(nrCalls).to.equal(1); + }); + }) + .then(() => promiseDelay(75)) + .then(() => { + return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) + .then((working) => { + expect(working).to.equal(false); + expect(nrCalls).to.equal(2); + }); }); - }); }); it('should not cache is-online errors', () => { @@ -215,22 +215,22 @@ describe('connectivity cache', () => { const isLinkWorking = mockRequire.reRequire('../'); return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then(() => { - throw new Error('Should have failed'); - }, () => { - mockRequire('is-online', (callback) => { - nrCalls += 1; - return callback(null, true); + .then(() => { + throw new Error('Should have failed'); + }, () => { + mockRequire('is-online', (callback) => { + nrCalls += 1; + return callback(null, true); + }); + + const isLinkWorking = mockRequire.reRequire('../'); + + return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) + .then((working) => { + expect(nrCalls).to.equal(2); + expect(working).to.equal(false); + }); }); - - const isLinkWorking = mockRequire.reRequire('../'); - - return isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }) - .then((working) => { - expect(nrCalls).to.equal(2); - expect(working).to.equal(false); - }); - }); }); it('should not cache if connectivityCacheDuration is 0', () => { @@ -246,16 +246,16 @@ describe('connectivity cache', () => { isLinkWorking.connectivityCacheDuration = 0; return Promise.resolve() - .then(() => { - return Promise.all([ - isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), - isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), - promiseDelay(50).then(() => isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true })), - ]) - .then((working) => { - expect(working).to.eql([false, false, false]); - expect(nrCalls).to.equal(3); + .then(() => { + return Promise.all([ + isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), + isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true }), + promiseDelay(50).then(() => isLinkWorking('http://thisdomainwillneverexist.org', { checkConnectivity: true })), + ]) + .then((working) => { + expect(working).to.eql([false, false, false]); + expect(nrCalls).to.equal(3); + }); }); - }); }); });