Skip to content

Commit

Permalink
Merge pull request #77 from justindhillon/file&line
Browse files Browse the repository at this point in the history
CLI outputs broken link, path, and line number.
  • Loading branch information
justindhillon authored Feb 25, 2024
2 parents 99fe91f + a415a15 commit e603124
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 27 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The `arg` can be a link, file path, or directory path. The `callback` will be gi
import linkInspector from 'link-inspector';

linkInspector('http://example.com', function (link) {
console.log(`Broken link found: ${link}`);
console.log(`Broken link found: ${link}`);
});
```

Expand All @@ -27,8 +27,8 @@ If you want to use linkInspector on all the files in a directory:
```js
import linkInspector from 'link-inspector';

linkInspector('./path/to/directory', function (link, path) {
console.log(`Broken link ${link} found in ${path}`);
linkInspector('./path/to/directory', function (link, path, lineNumber) {
console.log(`Broken link ${link} found in ${path} on line ${lineNumber}`);
});
```

Expand Down
14 changes: 10 additions & 4 deletions bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,31 @@ if (args.length === 0) {
console.error("no link or path given");
}

async function writeLink(link: string, path: string) {
async function writeLink(link: string, path: string, lineNumber: number) {
path = path.replace(/\/\//g, '/');

console.log("Broken Link:", link);
if (path) console.log("Path:", path);
if (lineNumber) console.log("Line:", lineNumber);
console.log("");

if (path) {
path = "output/" + path;
if (!fs.existsSync(path)) {
if (!fs.existsSync(path))
fs.mkdirSync(dirname(path), { recursive: true });
}

fs.appendFileSync(path, link + "\n");
}
}

for (const arg of args) {
let path: string = '';

try {new URL(arg)}
try { new URL(arg) }
catch {
path = basename(arg);
}

linkInspector(arg, writeLink, path);
console.log("");
}
6 changes: 4 additions & 2 deletions checkLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ export async function checkLink(link: string): Promise<boolean> {
await axios.head(link, params);
} catch (err: any) {
// If false positive, return false
if (falsePositives.has(err.response.status)) return false;
if (falsePositives.has(err.response.status))
return false;

// If HEAD is not allowed try GET
if (err.response.status === 405) {
try {
await axios.get(link, params);
} catch (error: any) {
// If false positive, return false
if (falsePositives.has(err.response.status)) return false;
if (falsePositives.has(err.response.status))
return false;

return true;
}
Expand Down
44 changes: 26 additions & 18 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { checkLink } from "./checkLink";
import fs from 'fs';

const urlRegex: RegExp = /(?<!xmlns=['"])(?<!xmlns:.*=['"])(?<!targetNamespace=['"])(\bhttps?:\/\/(?!.*\$)(?!.*{)(?!.*"\s\+)[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;

let QUEUE:Record<number, string[]> = {};
let PROCESS: number = 0;
let CURRENTPROCESS: number = 0;
Expand All @@ -11,14 +13,13 @@ async function runProcess(callback: any) {
if (MAXPROCESSES <= RUNNINGPROCESSES || !QUEUE[PROCESS-1]) return;

RUNNINGPROCESSES++;
const [link, path] = QUEUE[CURRENTPROCESS]!;
const [link, path, lineNumber] = QUEUE[CURRENTPROCESS]!;
delete QUEUE[CURRENTPROCESS];
CURRENTPROCESS++;

try {
if (await checkLink(link!)) {
callback(link, path);
}
if (await checkLink(link!))
callback(link, path, lineNumber);
} catch {} // Skip invalid urls

RUNNINGPROCESSES--;
Expand All @@ -38,10 +39,12 @@ export default async function linkInspector(arg: string, callback: any, path: st
const stats: fs.Stats = fs.lstatSync(arg);

// Skip symbolic links
if (stats.isSymbolicLink()) return;
if (stats.isSymbolicLink())
return;

// Skip files over 100mb
if (100*1024*1024 < stats.size) return
if (100*1024*1024 < stats.size)
return

// Handle directory
if (stats.isDirectory()) {
Expand All @@ -56,22 +59,27 @@ export default async function linkInspector(arg: string, callback: any, path: st
const content: string = fs.readFileSync(arg, 'utf8');

// Skip binary files
if (!/^[\x00-\x7F]*$/.test(content)) return;
if (!/^[\x00-\x7F]*$/.test(content))
return;

// Get all the links
const urlRegex: RegExp = /(?<!xmlns=['"])(?<!xmlns:.*=['"])(?<!targetNamespace=['"])(\bhttps?:\/\/(?!.*\$)(?!.*{)(?!.*"\s\+)[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
const links: string[] = content.match(urlRegex) || [];
const lines = content.split('\n');

for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const matches = line!.match(urlRegex) || [];

const directoryIndex: number = arg.indexOf(path);
const pathAfterDirectory: string = arg.substring(directoryIndex);
const directoryIndex: number = arg.indexOf(path);
const pathAfterDirectory: string = arg.substring(directoryIndex);

for (const link of links) {
try { // Runs link inspector on each link
new URL(link);
QUEUE[PROCESS] = [link, pathAfterDirectory];
PROCESS++;
runProcess(callback);
} catch {}
for (const link of matches) {
try { // Runs link inspector on each link
new URL(link);
QUEUE[PROCESS] = [link, pathAfterDirectory, (i+1).toString()];
PROCESS++;
runProcess(callback);
} catch {}
}
}
} catch {
console.error(`Error: ${arg} is not a valid link or path`);
Expand Down

0 comments on commit e603124

Please sign in to comment.