Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

linkinator

Package Overview
Dependencies
Maintainers
1
Versions
114
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

linkinator - npm Package Compare versions

Comparing version 1.3.0 to 1.3.1

116

build/src/cli.js
#!/usr/bin/env node
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -52,61 +44,59 @@ const meow = require("meow");

skip: { type: 'string', alias: 's' },
format: { type: 'string', alias: 'f' }
}
format: { type: 'string', alias: 'f' },
},
});
let flags;
function main() {
return __awaiter(this, void 0, void 0, function* () {
if (cli.input.length !== 1) {
cli.showHelp();
return;
async function main() {
if (cli.input.length !== 1) {
cli.showHelp();
return;
}
flags = cli.flags;
const start = Date.now();
log(`🏊‍♂️ crawling ${cli.input}`);
const checker = new index_1.LinkChecker();
checker.on('pagestart', url => {
log(`\n Scanning ${chalk_1.default.grey(url)}`);
});
checker.on('link', (link) => {
let state = '';
switch (link.state) {
case index_1.LinkState.BROKEN:
state = `[${chalk_1.default.red(link.status.toString())}]`;
break;
case index_1.LinkState.OK:
state = `[${chalk_1.default.green(link.status.toString())}]`;
break;
case index_1.LinkState.SKIPPED:
state = `[${chalk_1.default.grey('SKP')}]`;
break;
default:
throw new Error('Invalid state.');
}
flags = cli.flags;
const start = Date.now();
log(`🏊‍♂️ crawling ${cli.input}`);
const checker = new index_1.LinkChecker();
checker.on('pagestart', url => {
log(`\n Scanning ${chalk_1.default.grey(url)}`);
});
checker.on('link', (link) => {
let state = '';
switch (link.state) {
case index_1.LinkState.BROKEN:
state = `[${chalk_1.default.red(link.status.toString())}]`;
break;
case index_1.LinkState.OK:
state = `[${chalk_1.default.green(link.status.toString())}]`;
break;
case index_1.LinkState.SKIPPED:
state = `[${chalk_1.default.grey('SKP')}]`;
break;
default:
throw new Error('Invalid state.');
}
log(` ${state} ${chalk_1.default.gray(link.url)}`);
});
const opts = { path: cli.input[0], recurse: cli.flags.recurse };
if (cli.flags.skip) {
const skips = cli.flags.skip;
opts.linksToSkip = skips.split(' ').filter(x => !!x);
}
const result = yield checker.check(opts);
log();
const format = flags.format ? flags.format.toLowerCase() : null;
if (format === 'json') {
console.log(result);
return;
}
else if (format === 'csv') {
const csv = yield toCSV(result.links);
console.log(csv);
return;
}
const total = (Date.now() - start) / 1000;
if (!result.passed) {
const borked = result.links.filter(x => x.state === index_1.LinkState.BROKEN);
console.error(chalk_1.default.bold(`${chalk_1.default.red('ERROR')}: Detected ${borked.length} broken links. Scanned ${chalk_1.default.yellow(result.links.length.toString())} links in ${chalk_1.default.cyan(total.toString())} seconds.`));
process.exit(1);
}
log(chalk_1.default.bold(`🤖 Successfully scanned ${chalk_1.default.green(result.links.length.toString())} links in ${chalk_1.default.cyan(total.toString())} seconds.`));
log(` ${state} ${chalk_1.default.gray(link.url)}`);
});
const opts = { path: cli.input[0], recurse: cli.flags.recurse };
if (cli.flags.skip) {
const skips = cli.flags.skip;
opts.linksToSkip = skips.split(' ').filter(x => !!x);
}
const result = await checker.check(opts);
log();
const format = flags.format ? flags.format.toLowerCase() : null;
if (format === 'json') {
console.log(result);
return;
}
else if (format === 'csv') {
const csv = await toCSV(result.links);
console.log(csv);
return;
}
const total = (Date.now() - start) / 1000;
if (!result.passed) {
const borked = result.links.filter(x => x.state === index_1.LinkState.BROKEN);
console.error(chalk_1.default.bold(`${chalk_1.default.red('ERROR')}: Detected ${borked.length} broken links. Scanned ${chalk_1.default.yellow(result.links.length.toString())} links in ${chalk_1.default.cyan(total.toString())} seconds.`));
process.exit(1);
}
log(chalk_1.default.bold(`🤖 Successfully scanned ${chalk_1.default.green(result.links.length.toString())} links in ${chalk_1.default.cyan(total.toString())} seconds.`));
}

@@ -113,0 +103,0 @@ function log(message = '\n') {

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -32,29 +24,27 @@ const events_1 = require("events");

*/
check(options) {
return __awaiter(this, void 0, void 0, function* () {
options.linksToSkip = options.linksToSkip || [];
options.linksToSkip.push('^mailto:');
let server;
if (!options.path.startsWith('http')) {
const port = options.port || 5000 + Math.round(Math.random() * 1000);
server = yield this.startWebServer(options.path, port);
enableDestroy(server);
options.path = `http://localhost:${port}`;
}
const results = yield this.crawl({
url: options.path,
crawl: true,
checkOptions: options,
results: [],
cache: new Set()
});
const result = {
links: results,
passed: results.filter(x => x.state === LinkState.BROKEN).length === 0
};
if (server) {
server.destroy();
}
return result;
async check(options) {
options.linksToSkip = options.linksToSkip || [];
options.linksToSkip.push('^mailto:');
let server;
if (!options.path.startsWith('http')) {
const port = options.port || 5000 + Math.round(Math.random() * 1000);
server = await this.startWebServer(options.path, port);
enableDestroy(server);
options.path = `http://localhost:${port}`;
}
const results = await this.crawl({
url: options.path,
crawl: true,
checkOptions: options,
results: [],
cache: new Set(),
});
const result = {
links: results,
passed: results.filter(x => x.state === LinkState.BROKEN).length === 0,
};
if (server) {
server.destroy();
}
return result;
}

@@ -70,3 +60,4 @@ /**

return new Promise(resolve => {
const server = http.createServer(ecstatic({ root }))
const server = http
.createServer(ecstatic({ root }))
.listen(port, () => resolve(server));

@@ -81,76 +72,73 @@ });

*/
crawl(opts) {
return __awaiter(this, void 0, void 0, function* () {
// Check to see if we've already scanned this url
if (opts.cache.has(opts.url)) {
return opts.results;
}
opts.cache.add(opts.url);
// Check for links that should be skipped
const skips = opts.checkOptions.linksToSkip
.map(linkToSkip => {
return (new RegExp(linkToSkip)).test(opts.url);
})
.filter(match => !!match);
if (skips.length > 0) {
const result = { url: opts.url, state: LinkState.SKIPPED };
opts.results.push(result);
this.emit('link', result);
return opts.results;
}
// Perform a HEAD or GET request based on the need to crawl
let status = 0;
let state = LinkState.BROKEN;
let data = '';
let shouldRecurse = false;
try {
let res = yield gaxios.request({
method: opts.crawl ? 'GET' : 'HEAD',
async crawl(opts) {
// Check to see if we've already scanned this url
if (opts.cache.has(opts.url)) {
return opts.results;
}
opts.cache.add(opts.url);
// Check for links that should be skipped
const skips = opts.checkOptions
.linksToSkip.map(linkToSkip => {
return new RegExp(linkToSkip).test(opts.url);
})
.filter(match => !!match);
if (skips.length > 0) {
const result = { url: opts.url, state: LinkState.SKIPPED };
opts.results.push(result);
this.emit('link', result);
return opts.results;
}
// Perform a HEAD or GET request based on the need to crawl
let status = 0;
let state = LinkState.BROKEN;
let data = '';
let shouldRecurse = false;
try {
let res = await gaxios.request({
method: opts.crawl ? 'GET' : 'HEAD',
url: opts.url,
responseType: opts.crawl ? 'text' : 'stream',
validateStatus: () => true,
});
// If we got an HTTP 405, the server may not like HEAD. GET instead!
if (res.status === 405) {
res = await gaxios.request({
method: 'GET',
url: opts.url,
responseType: opts.crawl ? 'text' : 'stream',
validateStatus: () => true
responseType: 'stream',
validateStatus: () => true,
});
// If we got an HTTP 405, the server may not like HEAD. GET instead!
if (res.status === 405) {
res = yield gaxios.request({
method: 'GET',
url: opts.url,
responseType: 'stream',
validateStatus: () => true
});
}
// Assume any 2xx status is 👌
status = res.status;
if (res.status >= 200 && res.status < 300) {
state = LinkState.OK;
}
data = res.data;
shouldRecurse = isHtml(res);
}
catch (err) {
// request failure: invalid domain name, etc.
// Assume any 2xx status is 👌
status = res.status;
if (res.status >= 200 && res.status < 300) {
state = LinkState.OK;
}
const result = { url: opts.url, status, state };
opts.results.push(result);
this.emit('link', result);
// If we need to go deeper, scan the next level of depth for links and crawl
if (opts.crawl && shouldRecurse) {
this.emit('pagestart', opts.url);
const urls = links_1.getLinks(data, opts.url);
for (const url of urls) {
// only crawl links that start with the same host
const crawl = opts.checkOptions.recurse &&
url.startsWith(opts.checkOptions.path);
yield this.crawl({
url,
crawl,
cache: opts.cache,
results: opts.results,
checkOptions: opts.checkOptions
});
}
data = res.data;
shouldRecurse = isHtml(res);
}
catch (err) {
// request failure: invalid domain name, etc.
}
const result = { url: opts.url, status, state };
opts.results.push(result);
this.emit('link', result);
// If we need to go deeper, scan the next level of depth for links and crawl
if (opts.crawl && shouldRecurse) {
this.emit('pagestart', opts.url);
const urls = links_1.getLinks(data, opts.url);
for (const url of urls) {
// only crawl links that start with the same host
const crawl = opts.checkOptions.recurse && url.startsWith(opts.checkOptions.path);
await this.crawl({
url,
crawl,
cache: opts.cache,
results: opts.results,
checkOptions: opts.checkOptions,
});
}
// Return the aggregate results
return opts.results;
});
}
// Return the aggregate results
return opts.results;
}

@@ -163,8 +151,6 @@ }

*/
function check(options) {
return __awaiter(this, void 0, void 0, function* () {
const checker = new LinkChecker();
const results = yield checker.check(options);
return results;
});
async function check(options) {
const checker = new LinkChecker();
const results = await checker.check(options);
return results;
}

@@ -171,0 +157,0 @@ exports.check = check;

@@ -17,4 +17,12 @@ "use strict";

src: [
'audio', 'embed', 'frame', 'iframe', 'img', 'input', 'script', 'source',
'track', 'video'
'audio',
'embed',
'frame',
'iframe',
'img',
'input',
'script',
'source',
'track',
'video',
],

@@ -33,3 +41,5 @@ srcset: ['img', 'source'],

});
const sanitized = links.filter(link => !!link).map(link => normalizeLink(link, baseUrl));
const sanitized = links
.filter(link => !!link)
.map(link => normalizeLink(link, baseUrl));
return sanitized;

@@ -41,3 +51,5 @@ }

case 'srcset':
return value.split(',').map((pair) => pair.trim().split(/\s+/)[0]);
return value
.split(',')
.map((pair) => pair.trim().split(/\s+/)[0]);
default:

@@ -44,0 +56,0 @@ return [value];

{
"name": "linkinator",
"description": "Find broken links, missing images, etc in your HTML. Scurry around your site and find all those broken links.",
"version": "1.3.0",
"version": "1.3.1",
"license": "MIT",

@@ -26,3 +26,3 @@ "repository": "JustinBeckwith/linkinator",

"ecstatic": "^4.0.0",
"gaxios": "^1.7.0",
"gaxios": "^2.0.0",
"jsonexport": "^2.4.1",

@@ -45,3 +45,3 @@ "meow": "^5.0.0",

"codecov": "^3.2.0",
"gts": "^0.9.0",
"gts": "^1.0.0",
"mocha": "^6.0.0",

@@ -48,0 +48,0 @@ "nock": "^10.0.6",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc