Skip to content

Commit

Permalink
Add tests, switch to bun, outsource regex
Browse files Browse the repository at this point in the history
  • Loading branch information
spookyuser committed Jan 2, 2025
1 parent 57999ab commit 1ec9870
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 1,321 deletions.
22 changes: 22 additions & 0 deletions build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { BuildConfig } from "bun";

const defaultBuildConfig: BuildConfig = {
entrypoints: ["./index.ts"],

outdir: "./dist",
target: "node",
};

await Promise.all([
Bun.build({
...defaultBuildConfig,

format: "esm",
naming: "[dir]/[name].js",
}),
Bun.build({
...defaultBuildConfig,
format: "cjs",
naming: "[dir]/[name].cjs",
}),
]);
Binary file added bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions folder/fixture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// An example file that this might run on
const script = `https://apis.google.com/js/api.js?q=${Date.now()}`;
fetch(`https://apis.google.com/js/api.js?onload=${a}*`).catch((c = ""));
("https://www.google.com/recaptcha/enterprise.js?render=");
39 changes: 39 additions & 0 deletions index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { expect, test } from "bun:test";
import { extractJsLinks } from "./index";
import fs from "fs";

// More or less from https://github.com/kevva/url-regex/blob/master/test.js

test("match js URLs in text", () => {
const fixture = `
<a href="http://example.com/file.js">example.com</a>
<a href="http://example.com/with-path/file.js">with path</a>
[and another](https://another.example.com/file.min.js) and
https://another.example.com/file.min.js?withqueryparams=true
`;

expect(extractJsLinks(fixture)).toEqual([
"http://example.com/file.js",
"http://example.com/with-path/file.js",
"https://another.example.com/file.min.js",
"https://another.example.com/file.min.js?withqueryparams=true",
]);
});

test("dont't match these urls", () => {
const fixture = `
"https://www.googletagmanager.com/gtag/js"
`;

expect(extractJsLinks(fixture)).toEqual([]);
});

test("match js URLs in js", () => {
const fixture = fs.readFileSync("./folder/fixture.js", "utf-8");

expect(extractJsLinks(fixture)).toEqual([
"https://apis.google.com/js/api.js?q=$",
"https://apis.google.com/js/api.js?onload=$",
"https://www.google.com/recaptcha/enterprise.js?render=",
]);
});
107 changes: 107 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env node

import fs from "fs";
import path from "path";
import arg from "arg";
import invariant from "tiny-invariant";

const urlRegex =
/((?<!\+)https?:\/\/(?:www\.)?(?:[-\p{Letter}.]+?[.@][a-zA-Z\d]{2,}|localhost)(?:[-\w\p{Letter}.:%+~#*$!?&/=@]*?(?:,(?!\s))*?)*)/gu;

const helpText = `
Description:
a cli to find and remove links that might annoy the chrome web store team
often these are firebase links for some reason
Usage:
badlinks <file_or_folder> [options]
Options:
-r, --remove Remove the found JS links
-h, --help Display this help message
`;

function showHelp() {
console.log(helpText);
process.exit(0);
}

export function extractJsLinks(text: string) {
return text.match(urlRegex)?.filter((link) => link.includes(".js")) || [];
}

function processFile(filePath: string, shouldRemove: boolean) {
let fileContent = fs.readFileSync(filePath, "utf-8");
const jsLinks = extractJsLinks(fileContent);

if (jsLinks.length > 0) {
console.log(`JS link(s) found in ${filePath}:`);
console.log(jsLinks);

if (shouldRemove) {
for (const link of jsLinks) {
fileContent = fileContent.replace(link, "");
}
fs.writeFileSync(filePath, fileContent);
console.log(`JS link(s) removed from ${filePath}`);
} else {
console.error("Links found (use --remove to remove them).");
}
}
}

function processPath(inputPath: string, shouldRemove: boolean) {
const fileStats = fs.statSync(inputPath);

if (fileStats.isDirectory()) {
const fileNames = fs.readdirSync(inputPath);
for (const fileName of fileNames) {
processPath(path.join(inputPath, fileName), shouldRemove);
}
} else if (fileStats.isFile() && path.extname(inputPath) === ".js") {
processFile(inputPath, shouldRemove);
}
}

function parseArgs() {
const argSpec = {
"--help": Boolean,
"--remove": Boolean,
"-h": "--help",
"-r": "--remove",
};

try {
const args = arg(argSpec);

if (args["--help"]) {
showHelp();
}

const inputPath = args._.length > 0 ? args._[0] : null;

if (!inputPath) {
console.error("** Error: A file or folder path is required \n --- ");
showHelp();
}

return {
inputPath,
remove: args["--remove"] || false,
};
} catch (error) {
// @ts-ignore
console.error(`Error: ${error?.message}`);
showHelp();
}
}

try {
const options = parseArgs();
invariant(options?.inputPath, "No input path");
processPath(options.inputPath, options.remove);
} catch (error) {
// @ts-ignore
console.error("Error:", error.message);
process.exit(1);
}
39 changes: 16 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
{
"name": "badlinks",
"version": "1.0.11",
"version": "1.1.0",
"description": "A fast way to scan a package for links that might upset the chrome webstore team",
"scripts": {
"build": "tsup ./src",
"start": "node ./dist/index.js"
"build": "bun run build.ts",
"start": "bun run dist/index.js",
"dev": "bun run ./index.ts",
"prepublishOnly": "bun run build"
},
"devDependencies": {
"arg": "^5.0.2",
"tiny-invariant": "^1.3.3",
"typescript": "^5.7.2",
"@types/bun": "latest"
},
"type": "module",
"homepage": "https://github.com/spookyuser/badlinks",
Expand All @@ -18,29 +26,14 @@
],
"author": "spookyuser",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.17.7",
"tsup": "^6.7.0",
"typescript": "^5.7.2"
},
"files": [
"dist",
"package.json"
"dist"
],
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"default": "./dist/index.js",
"node": "./dist/index.js"
}
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"dependencies": {
"commander": "^11.1.0"
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"bin": {
"badlinks": "./dist/index.js"
Expand Down
Loading

0 comments on commit 1ec9870

Please sign in to comment.