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

check-html-links

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

check-html-links - npm Package Compare versions

Comparing version 0.2.3 to 0.2.4

src/checkLinks.js

16

CHANGELOG.md
# check-html-links
## 0.2.4
### Patch Changes
- 97d5fb2: Add external links validation via the flag `--validate-externals`.
You can/should provide an optional `--absolute-base-url` to handle urls starting with it as internal.
```bash
# check external urls
npx check-html-links _site --validate-externals
# check external urls but treat links like https://rocket.modern-web.dev/about/ as internal
npx check-html-links _site --validate-externals --absolute-base-url https://rocket.modern-web.dev
```
## 0.2.3

@@ -4,0 +20,0 @@

8

dist-types/types/main.d.ts

@@ -29,11 +29,13 @@ export interface Link {

interface Options {
export interface Options {
ignoreLinkPatterns: string[] | null;
validateExternals: boolean;
absoluteBaseUrl: string;
}
export interface CheckHtmlLinksCliOptions {
export interface CheckHtmlLinksCliOptions extends Options {
printOnError: boolean;
rootDir: string;
ignoreLinkPatterns: string[] | null;
continueOnError: boolean;
absoluteBaseUrl: string;
}
{
"name": "check-html-links",
"version": "0.2.3",
"version": "0.2.4",
"publishConfig": {

@@ -40,4 +40,5 @@ "access": "public"

"minimatch": "^3.0.4",
"node-fetch": "^3.0.0",
"sax-wasm": "^2.0.0",
"slash": "^3.0.0"
"slash": "^4.0.0"
},

@@ -44,0 +45,0 @@ "devDependencies": {

@@ -7,3 +7,3 @@ # Check HTML Links

```
```shell
npm i -D check-html-links

@@ -14,7 +14,7 @@ ```

```
```bash
npx check-html-links _site
```
For docs please see our homepage [https://rocket.modern-web.dev/docs/tools/check-html-links/](https://rocket.modern-web.dev/docs/tools/check-html-links/).
For docs please see our homepage [https://rocket.modern-web.dev/tools/check-html-links/overview/](https://rocket.modern-web.dev/tools/check-html-links/overview/).

@@ -21,0 +21,0 @@ ## Comparison

@@ -9,3 +9,3 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */

import commandLineArgs from 'command-line-args';
import { validateFiles } from './validateFolder.js';
import { prepareFiles, validateFiles } from './validateFolder.js';
import { formatErrors } from './formatErrors.js';

@@ -22,3 +22,5 @@ import { listFiles } from './listFiles.js';

{ name: 'root-dir', type: String, defaultOption: true },
{ name: 'continue-on-error', type: Boolean, defaultOption: false },
{ name: 'continue-on-error', type: Boolean },
{ name: 'validate-externals', type: Boolean },
{ name: 'absolute-base-url', type: String },
];

@@ -34,2 +36,4 @@ const options = commandLineArgs(mainDefinitions, {

ignoreLinkPatterns: options['ignore-link-pattern'],
validateExternals: options['validate-externals'],
absoluteBaseUrl: options['absolute-base-url'],
};

@@ -49,19 +53,44 @@ }

async run() {
const { ignoreLinkPatterns, rootDir: userRootDir } = this.options;
const {
ignoreLinkPatterns,
rootDir: userRootDir,
validateExternals,
absoluteBaseUrl,
} = this.options;
const rootDir = userRootDir ? path.resolve(userRootDir) : process.cwd();
const performanceStart = process.hrtime();
console.log('👀 Checking if all internal links work...');
const files = await listFiles('**/*.html', rootDir);
console.log('Check HTML Links');
const filesOutput =
files.length == 0
? '🧐 No files to check. Did you select the correct folder?'
: `🔥 Found a total of ${chalk.green.bold(files.length)} files to check!`;
? ' 🧐 No files to check. Did you select the correct folder?'
: ` 📖 Found ${chalk.green.bold(files.length)} files containing`;
console.log(filesOutput);
const { errors, numberLinks } = await validateFiles(files, rootDir, { ignoreLinkPatterns });
const { numberLinks, checkLocalFiles, checkExternalLinks } = await prepareFiles(
files,
rootDir,
{
ignoreLinkPatterns,
validateExternals,
absoluteBaseUrl,
},
);
console.log(`🔗 Found a total of ${chalk.green.bold(numberLinks)} links to validate!\n`);
console.log(` 🔗 ${chalk.green.bold(numberLinks)} internal links`);
if (validateExternals) {
console.log(` 🌐 ${chalk.green.bold(checkExternalLinks.length)} external links`);
}
console.log(' 👀 Checking...');
const { errors } = await validateFiles({
checkLocalFiles,
validateExternals,
checkExternalLinks,
});
const performance = process.hrtime(performanceStart);

@@ -77,3 +106,3 @@ /** @type {string[]} */

output = [
`❌ Found ${chalk.red.bold(
` ❌ Found ${chalk.red.bold(
errors.length.toString(),

@@ -86,3 +115,3 @@ )} missing reference targets (used by ${referenceCount} links) while checking ${

.map(line => ` ${line}`),
`Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
` 🕑 Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
];

@@ -98,3 +127,3 @@ message = output.join('\n');

console.log(
`✅ All internal links are valid. (executed in ${performance[0]}s ${
` ✅ All tested links are valid. (executed in ${performance[0]}s ${
performance[1] / 1000000

@@ -101,0 +130,0 @@ }ms)`,

@@ -18,3 +18,3 @@ import path from 'path';

output.push(
`${number}. missing ${chalk.red.bold(
` ${number}. missing ${chalk.red.bold(
`id="${error.usage[0].anchor}"`,

@@ -28,3 +28,3 @@ )} in ${chalk.cyanBright(filePath)}`,

output.push(`${number}. missing ${title} ${chalk.red.bold(filePath)}`);
output.push(` ${number}. missing ${title} ${chalk.red.bold(filePath)}`);
}

@@ -39,7 +39,7 @@ const usageLength = error.usage.length;

const attributeEnd = chalk.gray('"');
output.push(` from ${clickAbleLink} via ${attributeStart}${usage.value}${attributeEnd}`);
output.push(` from ${clickAbleLink} via ${attributeStart}${usage.value}${attributeEnd}`);
}
if (usageLength > 3) {
const more = chalk.red((usageLength - 3).toString());
output.push(` ... ${more} more references to this target`);
output.push(` ... ${more} more references to this target`);
}

@@ -46,0 +46,0 @@ output.push('');

@@ -6,6 +6,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */

import { createRequire } from 'module';
import { listFiles } from './listFiles.js';
import path from 'path';
import slash from 'slash';
import { listFiles } from './listFiles.js';
import { checkLinks } from './checkLinks.js';

@@ -33,2 +33,5 @@ /** @typedef {import('../types/main').Link} Link */

/** @type {Error[]} */
let checkExternalLinks = [];
/** @type {Error[]} */
let errors = [];

@@ -157,2 +160,22 @@

/**
* @param {string} filePath
* @param {Usage} usageObj
*/
function addExternalLink(filePath, usageObj) {
const foundIndex = checkExternalLinks.findIndex(item => {
return item.filePath === filePath;
});
if (foundIndex === -1) {
checkExternalLinks.push({
filePath,
onlyAnchorMissing: false,
usage: [usageObj],
});
} else {
checkExternalLinks[foundIndex].usage.push(usageObj);
}
}
/**
* @param {string} inValue

@@ -206,8 +229,13 @@ */

* @param {string} options.rootDir
* @param {string} options.absoluteBaseUrl
* @param {function(string): boolean} options.ignoreUsage
*/
async function resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage }) {
async function resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage, absoluteBaseUrl }) {
for (const hrefObj of links) {
const { value, anchor } = getValueAndAnchor(hrefObj.value);
const { value: rawValue, anchor } = getValueAndAnchor(hrefObj.value);
const value = rawValue.startsWith(absoluteBaseUrl)
? rawValue.substring(absoluteBaseUrl.length)
: rawValue;
const usageObj = {

@@ -236,4 +264,3 @@ attribute: hrefObj.attribute,

} else if (value.startsWith('//') || value.startsWith('http')) {
// TODO: handle external urls
// external url - we do not handle that (yet)
addExternalLink(htmlFilePath, usageObj);
} else if (value.startsWith('/')) {

@@ -252,3 +279,3 @@ const filePath = path.join(rootDir, valueFile);

return { checkLocalFiles: [...checkLocalFiles] };
return { checkLocalFiles: [...checkLocalFiles], checkExternalLinks: [...checkExternalLinks] };
}

@@ -293,2 +320,18 @@

/**
*
* @param {Error[]} checkExternalLinks
*/
async function validateExternalLinks(checkExternalLinks) {
for await (const localFileObj of checkExternalLinks) {
const links = localFileObj.usage.map(usage => usage.value);
const results = await checkLinks(links);
localFileObj.usage = localFileObj.usage.filter((link, index) => !results[index]);
if (localFileObj.usage.length > 0) {
errors.push(localFileObj);
}
}
return errors;
}
/**
* @param {string[]} files

@@ -298,3 +341,3 @@ * @param {string} rootDir

*/
export async function validateFiles(files, rootDir, opts) {
export async function prepareFiles(files, rootDir, opts) {
await parserReferences.prepareWasm(saxWasmBuffer);

@@ -305,2 +348,3 @@ await parserIds.prepareWasm(saxWasmBuffer);

checkLocalFiles = [];
checkExternalLinks = [];
idCache = new Map();

@@ -321,7 +365,23 @@ let numberLinks = 0;

numberLinks += links.length;
await resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage });
await resolveLinks(links, {
htmlFilePath,
rootDir,
ignoreUsage,
absoluteBaseUrl: opts?.absoluteBaseUrl,
});
}
return { checkLocalFiles, checkExternalLinks, numberLinks };
}
/**
* @param {*} param0
* @returns
*/
export async function validateFiles({ checkLocalFiles, validateExternals, checkExternalLinks }) {
await validateLocalFiles(checkLocalFiles);
if (validateExternals) {
await validateExternalLinks(checkExternalLinks);
}
return { errors: errors, numberLinks: numberLinks };
return { errors };
}

@@ -336,4 +396,12 @@

const files = await listFiles('**/*.html', rootDir);
const { errors } = await validateFiles(files, rootDir, opts);
const { checkLocalFiles, checkExternalLinks } = await prepareFiles(files, rootDir, opts);
const { errors } = await validateFiles({
checkLocalFiles,
validateExternals: opts?.validateExternals,
checkExternalLinks,
});
return errors;
}
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