npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scorpion2272/seo-defect-scanner

v1.1.3

Published

Seo defect scanner

Readme

SEO-defect-scanner

A npm module that let developers use this package to scan a HTML file and show all of the SEO defects.

Quick Start

  1. npm install @scorpion2272/seo-defect-scanner
  2. Use the pre-defined rule 1 mentioned above
    var fs = require('fs');
    var seoScanner = require("@scorpion2272/SEO-defect-scanner")
    new seoScanner()
    .addPredefinedRule1()
    .scan('input.html') // html file to be scanned
  3. The console output should be like this:

There are 2 tag without alt attribute

Pre-defined Rules

There are 5 pre-defined SEO rules here:

addPredefinedRule1()

Detect if any<img/> tag without alt attribute

new seoScanner().addPredefinedRule1()

addPredefinedRule2()

Detect if any<a /> tag without rel attribute

new seoScanner().addPredefinedRule2()

addPredefinedRule3()

Contains 3 subrules:

In <head> tag

  1. Detect if header doesn’t have <title> tag
  2. Detect if header doesn’t have <meta name=“descriptions” … /> tag
  3. Detect if header doesn’t have <meta name=“keywords” … /> tag
new seoScanner().addPredefinedRule3()

addPredefinedRule4(N)

Detect if there’re more than N <strong> tag in HTML (N is configurable)

new seoScanner().addPredefinedRule4(15) // Detect if there're more than 15 <strong> tag

addPredefinedRule5()

Detect if a HTML have more than one <H1> tag.

new seoScanner().addPredefinedRule5()

Customized Rule

You can manage your customized rule

addRule(rule, id)

Register a rule to scanner

new seoScanner()
.addRule(CustomizedRule1, "MoreThanOneH2Tag")

Your customized rule should be implemented like this:

function CustomizedRule1($) {
    if($('h2').length > 1) {
        return "HTML have more than one <H2> tag";
    }
}
  1. The input parameter $ will be passed by the scanner. It's similar to the jQuery DOM object, so you can search for the DOM element via jQuery selector syntax.
  2. Return a message when the defect is found.

removeRule(id)

Remove specified registered rule in scanner

new seoScanner()
.addRule(CustomizedRule1, "MoreThanOneH2Tag")
.removeRule("MoreThanOneH2Tag")

resetRule()

Clear all rules registered in scanner

new seoScanner()
.addRule(CustomizedRule1, "MoreThanOneH2Tag")
.resetRule()

Chained Rule Scanning

You can chain any pre-defined rules and customized rules to do the scan.

new seoScanner()
.addPredefinedRule1()
.addPredefinedRule4(10)
.addRule(CustomizedRule1, "MoreThanOneH2Tag")
.scan('input.html')

Configuration

When you create an instance of the scanner, you can pass a object as config:

var config = {
    inputType:'file', // file (default) or stream
    outputType:'console' // console (default) or file or stream
};
new seoScanner(config)

inputType: 'file' (Default)

You should pass the file path as the first parameter of scan()

new seoScanner({inputType:'file'}).scan('input.html')

inputType: 'stream'

You should pass a Node readable stream as the first parameter of scan()

var inputStream = fs.createReadStream('input.html')
new seoScanner({inputType:'stream'}).scan(inputStream)

outputType: 'console' (Default)

The scan output will be shown in the console.

new seoScanner({outputType:'console'}).scan('input.html')

outputType: 'file'

You should pass the file path as the second parameter of scan()

new seoScanner({outputType:'file'}).scan('input.html', 'output')

outputType: 'stream'

You should pass a Node writable stream as the second parameter of scan()

const outputStream = new Writable({
    write(chunk, encoding, callback) {
        console.log(chunk.toString())
    }
});
new seoScanner({outputType:'stream'}).scan('input.html', outputStream)

Appendix

Flexibility of pre-defined rule 3:

Use a object to define the jQuery selector, condition and message when defect is scanned, so developers who want to add additional subrules can just extend the object.

this.subRules = [
{
    selector: 'head title',
    shouldExist: true,
    message: 'Header doesn’t have <title> tag'
},
{
    selector: 'head meta[name="descriptions"]',
    shouldExist: true,
    message: 'Header doesn’t have <meta name=“descriptions” … /> tag'
},
{
    selector: 'head meta[name="keywords"]',
    shouldExist: true,
    message: 'Header doesn’t have <meta name=“keywords” … /> tag'
}
];

function scan($, subRules) {
    var result = [];

    subRules.forEach(function(subRule) {
        if($(subRule.selector).length!=0 != subRule.shouldExist) {
            result.push(subRule.message);
        }
    });

    return result;
}