Skip to content

Commit

Permalink
Added following changes-
Browse files Browse the repository at this point in the history
1. Configuration to ignore protocol
2. Test coverage 100%
  • Loading branch information
stackblogger committed May 23, 2023
1 parent 53c08c7 commit da9b108
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 29 deletions.
33 changes: 26 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/stackblogger/link-exists/master.yml?style=flat-square&logo=github&color=success)
![npm](https://img.shields.io/npm/v/link-exists?style=flat-square&color=success&logo=npm)
![npm](https://img.shields.io/npm/dw/link-exists?style=flat-square&color=success&logo=npm)
![npm bundle size](https://img.shields.io/bundlephobia/min/link-exists?style=flat-square&logo=npm)
![GitHub](https://img.shields.io/github/license/stackblogger/link-exists?style=flat-square&logo=github&color=success)

# link-exists

A simple JavaScript / TypeScript library to check whether a given url is valid and exists or not.
A super lightweight JavaScript / TypeScript library to check whether a given url is valid and exists or not.

## Installation

```bash
npm install link-exists
```

## Features

A super lightweight library to validate if a given url is valid or not. Some additional features are-

- less than <b>1 kb</b> in size
- additional configuration to validate as per custom requirement
- supports node.js latest version
- TypeScript and JavaScript support
- built on ES6 modules
- Jest test cases with <b>100% coverage</b>
- Promise based result

## Usage (TypeScript)

```typescript
import { linkExists } from 'link-exists';

const result = await linkExists('https://stackblogger.com');
console.log(result);

// OUTPUT true

const result = await linkExists('https://some-invalid-url.com');
console.log(result);

// OUTPUT false

const result = await linkExists('stackblogger.com');
console.log(result);

// OUTPUT false

// Configuration
const result = await linkExists('stackblogger.com', { ignoreProtocol: true });
console.log(result);
// OUTPUT true
```

## Usage (JavaScript)
Expand All @@ -39,18 +56,20 @@ const { linkExists } = require('link-exists');

const result = await linkExists('https://stackblogger.com');
console.log(result);

// OUTPUT true

const result = await linkExists('https://some-invalid-url.com');
console.log(result);

// OUTPUT false

const result = await linkExists('stackblogger.com');
console.log(result);

// OUTPUT false

// Configuration
const result = await linkExists('stackblogger.com', { ignoreProtocol: true });
console.log(result);
// OUTPUT true
```

### License
Expand Down
66 changes: 59 additions & 7 deletions __tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { linkExists } from '../src';
import { isValidUrl, reformUrl } from '../src/plugin/util';

describe('LinkExists', () => {
it('should exist', async () => {
const result = await linkExists('https://bookmymark.com');
expect(result).toBe(true);
describe('Validations', () => {
it('should validate link variable type', async () => {
const link = JSON.parse('{}');
try {
await linkExists(link);
} catch (error) {
expect(error).toStrictEqual(new TypeError(`Expected a string, got ${typeof link}`));
}
});

it('should not exist', async () => {
const result = await linkExists('https://sbbbookmymark.com');
expect(result).toBe(false);
it('should validate config type', async () => {
const value = JSON.parse('{}');
try {
await linkExists('https://bookmymark.com', { ignoreProtocol: value });
} catch (error) {
expect(error).toStrictEqual(new TypeError(`Expected a boolean, got ${typeof value}`));
}
});

it('should reform url', async () => {
Expand All @@ -22,11 +30,33 @@ describe('LinkExists', () => {
expect(result).toBe(null);
});

it('should reform url with includeProtocol true', async () => {
const result = reformUrl('bookmymark.com', true);
expect(result).toBeInstanceOf(URL);
});

it('should reform url with includeProtocol true', async () => {
const result = reformUrl('https://bookmymark.com', true);
expect(result).toBeInstanceOf(URL);
});

it('should not be a valid url', async () => {
const result = isValidUrl('some test url .com');
expect(result).toBe(false);
});

it('should not be a valid url', async () => {
const result = await linkExists('some test url .com');
expect(result).toBe(false);
});
});

describe('Link Exist', () => {
it('should exist', async () => {
const result = await linkExists('https://bookmymark.com');
expect(result).toBe(true);
});

it('should exist', async () => {
const result = await linkExists('https://www.evrig.com/blog/how-to-check-magento-version/');
expect(result).toBe(true);
Expand All @@ -51,4 +81,26 @@ describe('LinkExists', () => {
const result = await linkExists('https://www.fizzygoblet.com/collections/kolha-wedge');
expect(result).toBe(true);
});

it('should exist without protocol and with ignoreProtocol true', async () => {
const result = await linkExists('bookmymark.com', { ignoreProtocol: true });
expect(result).toBe(true);
});
});

describe('Link Not Exist', () => {
it('should not exist', async () => {
const result = await linkExists('https://sbbbookmymark.com');
expect(result).toBe(false);
});

it('should not exist without protocol and with ignoreProtocol false', async () => {
const result = await linkExists('bookmymark.com', { ignoreProtocol: false });
expect(result).toBe(false);
});

it('should not exist without protocol and without config', async () => {
const result = await linkExists('bookmymark.com');
expect(result).toBe(false);
});
});
24 changes: 17 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "link-exists",
"version": "1.2.1",
"description": "Checks whether a given link exists.",
"version": "1.2.2",
"description": "A super lightweight JavaScript / TypeScript library to check whether a given url is valid and exists or not.",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
Expand All @@ -23,9 +23,14 @@
},
"keywords": [
"link-exists",
"url-exist",
"url-validator",
"link-validator"
"url-exist",
"url-validator",
"link-validator",
"link-check",
"validator",
"verify",
"node.js",
"typescript"
],
"author": {
"name": "StackBlogger (Jameer Khan)",
Expand All @@ -36,7 +41,7 @@
"bugs": {
"url": "https://github.com/stackblogger/link-exists/issues"
},
"homepage": "https://github.com/stackblogger/link-exists#readme",
"homepage": "https://stackblogger.com",
"devDependencies": {
"@types/jest": "^28.1.6",
"@types/node": "^18.15.11",
Expand All @@ -53,7 +58,12 @@
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
"testEnvironment": "node",
"coverageThreshold": {
"global": {
"branches": 95
}
}
},
"engines": {
"node": ">= 16.0.0"
Expand Down
15 changes: 15 additions & 0 deletions src/plugin/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface LocalConfig {
/**
* default false.
*
* if `ignoreProtocol` is true, it will validate urls without https:// or http:// as fine.
*
* if `ignoreProtocol` is false (default), it will validate urls without https:// or http:// as not fine
*/
ignoreProtocol: boolean;
}

/**
* link validator configuration for custom validator
*/
export interface LinkValidatorConfig extends Partial<LocalConfig> {}
4 changes: 4 additions & 0 deletions src/plugin/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const REGEX_VALID_URL =
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;

export const REGEX_PREPEND_PROTOCOL = /^https?:\/\//i;
11 changes: 8 additions & 3 deletions src/plugin/link-exists.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import http from 'http';
import { isValidUrl, reformUrl } from './util';
import { LinkValidatorConfig } from './config';

/**
* Check whether a link exists or not.
*
* @param link string url to be checked
* @returns {boolean} returns whether the given link is reachable or not
* @param config optional configuration to run validator as per your custom requirement
* @returns {boolean} returns whether the given link is reachable or nots
*/
export async function linkExists(link: string): Promise<boolean> {
export async function linkExists(link: string, config?: LinkValidatorConfig): Promise<boolean> {
if (typeof link !== 'string') {
throw new TypeError(`Expected a string, got ${typeof link}`);
}
if (config && config.ignoreProtocol && typeof config.ignoreProtocol !== 'boolean') {
throw new TypeError(`Expected a boolean, got ${typeof config.ignoreProtocol}`);
}
const isItValid = isValidUrl(link);
if (typeof isItValid === 'boolean' && isItValid === false) return false;
const reformedUrl = reformUrl(link);
const reformedUrl = reformUrl(link, config?.ignoreProtocol);
if (!reformedUrl) return false;

const { host, pathname } = reformedUrl;
Expand Down
20 changes: 15 additions & 5 deletions src/plugin/util.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { REGEX_PREPEND_PROTOCOL, REGEX_VALID_URL } from './constants';

/**
* Format a string url with URL class
*
* @param url url to be reformed
* @param includeProtocol validate url with or without protocol
* @returns {URL | null} URL formatted object
*/
export function reformUrl(url: string): URL | null {
export function reformUrl(url: string, includeProtocol?: boolean): URL | null {
try {
return new URL(url.trim());
let localUrl = url;
if (typeof includeProtocol === 'boolean' && includeProtocol === true) {
localUrl = !REGEX_PREPEND_PROTOCOL.test(localUrl) ? `http://${localUrl}` : localUrl;
}
return new URL(localUrl.trim());
} catch (_e) {
return null;
}
}

const REGEX_VALID_URL =
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;

/**
* validates a url using javascript regex
*
* @param url url to validate
* @returns true / false whether a given url is valid
*/
export function isValidUrl(url: string): boolean {
return url.match(REGEX_VALID_URL) !== null;
}

0 comments on commit da9b108

Please sign in to comment.