New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

seord

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

seord - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

2

dist/html-analyzer.d.ts

@@ -10,2 +10,3 @@ import { type CheerioAPI } from "cheerio";

siteDomainName: string | null;
bodyText: string;
constructor(htmlContent: string, siteDomainName?: string | null);

@@ -19,3 +20,4 @@ getAllLinks(): Link[];

getInternalLinks(): LinksGroup;
getPureText(stringContent: string): string;
getWordCount(stringContent?: string | null): number;
}

14

dist/html-analyzer.js

@@ -13,2 +13,3 @@ "use strict";

this.siteDomainName = siteDomainName;
this.bodyText = this.htmlDom.text().toLowerCase();
}

@@ -82,10 +83,9 @@ getAllLinks() {

}
getPureText(stringContent) {
let gapSpaceRegex = /\s+/gi;
return stringContent.trim().replace(gapSpaceRegex, ' ');
}
getWordCount(stringContent = null) {
if (!stringContent) {
stringContent = this.htmlDom.text().toLowerCase();
}
else {
stringContent = stringContent.toLowerCase();
}
return stringContent.split(' ').length;
stringContent = stringContent ? stringContent.toLowerCase() : this.htmlDom.text().toLowerCase();
return this.getPureText(stringContent).split(' ').length;
}

@@ -92,0 +92,0 @@ }

@@ -26,6 +26,9 @@ interface Link {

seoScore: number;
wordCount: number;
keywordSeoScore: number;
keywordFrequency: number;
messages: {
warnings: string[];
goodPoints: string[];
minorWarnings: string[];
};

@@ -40,4 +43,5 @@ keywordDensity: number;

keywordWithTitle: KeywordDensity;
wordCount: number;
};
}
export { Link, LinksGroup, KeywordDensity, ContentJson, SeoData };

@@ -6,22 +6,61 @@ import type { HtmlAnalyzer } from "./html-analyzer";

MAXIMUM_KEYWORD_DENSITY: number;
MAXIMUM_SUB_KEYWORD_DENSITY: number;
MINIMUM_SUB_KEYWORD_DENSITY: number;
EXTREME_LOW_SUB_KEYWORD_DENSITY: number;
MAXIMUM_META_DESCRIPTION_LENGTH: number;
MAXIMUM_META_DESCRIPTION_DENSITY: number;
MINIMUM_META_DESCRIPTION_DENSITY: number;
MAXIMUM_TITLE_LENGTH: number;
MINIMUM_TITLE_LENGTH: number;
MAXIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY: number;
MINIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY: number;
content: ContentJson;
htmlAnalyzer: HtmlAnalyzer;
htmlDom: HtmlAnalyzer['htmlDom'];
bodyText: string;
keywordDensity: number;
messages: {
warnings: string[];
minorWarnings: string[];
goodPoints: string[];
};
constructor(content: ContentJson, htmlAnalyzer: HtmlAnalyzer);
getSubKeywordsDensity(): KeywordDensity[];
calculateDensity(keyword: string, bodyText?: string | null): number;
getKeywordDensity(): number;
totalUniqueInternalLinksCount(): number;
totalUniqueExternalLinksCount(): number;
getMessages(): {
warnings: string[];
goodPoints: string[];
};
getKeywordInTitle(keyword?: string | null): KeywordDensity;
getSubKeywordsInTitle(): KeywordDensity[];
countOccurrencesInString(keyword: string, stringContent: string): number;
getKeywordInMetaDescription(keyword?: string | null): KeywordDensity;
getSubKeywordsInMetaDescription(): KeywordDensity[];
getSeoScore(): number;
getKeywordSeoScore(): number;
getTitleWordCount(): number;
private assignMessagesForKeyword;
private assignMessagesForSubKeywords;
private assignMessagesForTitle;
private assignMessagesForLinks;
private assignMessagesForMetaDescription;
/**
* Returns the messages object.
* @return object The messages object.
* @example
* {
* goodPoints: [],
* warnings: [],
* minorWarnings: [],
* }
* @see SeoAnalyzer.messages
*/
private assignMessages;
/**
* Calculates the density of a keyword in the given string of body text.
* @param keyword Should not be null.
* @param bodyText If null, it will use the default value, i.e. `this.htmlAnalyzer.bodyText`
*/
calculateDensity(keyword: string, bodyText?: string | null): number;
/**
* Returns the number of occurrences of a keyword in a string. Or you can say, it returns the keyword count in the given string.
* @param keyword If null, it will use the default value, i.e. `this.content.keyword`
* @param stringContent If null, it will use the default value, i.e. `this.htmlAnalyzer.bodyText`
* @return number The number of occurrences of the keyword in the string.
*/
countOccurrencesInString(keyword?: string | null, stringContent?: string | null): number;
}

@@ -6,8 +6,23 @@ "use strict";

constructor(content, htmlAnalyzer) {
this.MINIMUM_KEYWORD_DENSITY = 0.5;
this.MAXIMUM_KEYWORD_DENSITY = 5;
this.MINIMUM_KEYWORD_DENSITY = 1;
this.MAXIMUM_KEYWORD_DENSITY = 3;
this.MAXIMUM_SUB_KEYWORD_DENSITY = 1;
this.MINIMUM_SUB_KEYWORD_DENSITY = 0.12;
this.EXTREME_LOW_SUB_KEYWORD_DENSITY = 0.09;
this.MAXIMUM_META_DESCRIPTION_LENGTH = 160;
this.MAXIMUM_META_DESCRIPTION_DENSITY = 5;
this.MINIMUM_META_DESCRIPTION_DENSITY = 2;
this.MAXIMUM_TITLE_LENGTH = 70;
this.MINIMUM_TITLE_LENGTH = 40;
this.MAXIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY = 5;
this.MINIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY = 2;
this.messages = {
warnings: [],
minorWarnings: [],
goodPoints: []
};
this.content = content;
this.htmlAnalyzer = htmlAnalyzer;
this.htmlDom = htmlAnalyzer.htmlDom;
this.bodyText = this.htmlDom.text().toLowerCase();
this.keywordDensity = this.calculateDensity(this.content.keyword);
this.assignMessages();
}

@@ -25,12 +40,2 @@ getSubKeywordsDensity() {

}
calculateDensity(keyword, bodyText = null) {
if (!bodyText) {
bodyText = this.bodyText;
}
let keywordCount = this.countOccurrencesInString(keyword, bodyText);
return (keywordCount / bodyText.split(' ').length) * 100;
}
getKeywordDensity() {
return this.calculateDensity(this.content.keyword);
}
totalUniqueInternalLinksCount() {

@@ -42,136 +47,257 @@ return this.htmlAnalyzer.getInternalLinks().unique.length;

}
getMessages() {
const warnings = [];
const goodPoints = [];
let keywordDensity = this.getKeywordDensity();
const wordCount = this.htmlAnalyzer.getWordCount();
getKeywordInTitle(keyword = null) {
var _a;
keyword = keyword !== null && keyword !== void 0 ? keyword : this.content.keyword;
const density = this.calculateDensity(keyword, this.content.title);
return {
keyword,
density,
position: (_a = this.content.title) === null || _a === void 0 ? void 0 : _a.indexOf(keyword)
};
}
getSubKeywordsInTitle() {
let subKeywordsInTitle = [];
this.content.subKeywords.forEach((sub_keyword) => {
subKeywordsInTitle.push(this.getKeywordInTitle(sub_keyword));
});
return subKeywordsInTitle;
}
getKeywordInMetaDescription(keyword = null) {
var _a;
if (keyword === null) {
keyword = this.content.keyword;
}
const density = this.calculateDensity(keyword, this.content.metaDescription);
return {
keyword,
density,
position: (_a = this.content.metaDescription) === null || _a === void 0 ? void 0 : _a.indexOf(keyword)
};
}
getSubKeywordsInMetaDescription() {
let subKeywordsInTitle = [];
this.content.subKeywords.forEach((sub_keyword) => {
subKeywordsInTitle.push(this.getKeywordInMetaDescription(sub_keyword));
});
return subKeywordsInTitle;
}
getSeoScore() {
const MAX_SCORE = 100;
const { warnings, goodPoints } = this.messages;
const messagesScore = ((goodPoints.length) / (warnings.length + goodPoints.length)) * 100;
return Math.min(messagesScore, MAX_SCORE); // SEO score should never go above 100
}
getKeywordSeoScore() {
const MAX_SCORE = 100;
const keywordInTitle = this.getKeywordInTitle();
const subKeywordsInTitle = this.getSubKeywordsInTitle();
const subKeywordsDensity = this.getSubKeywordsDensity();
const keywordInTitleScore = keywordInTitle.density * 10;
const subKeywordsInTitleScore = subKeywordsInTitle.length * 10;
const subKeywordsDensityScore = subKeywordsDensity.reduce((total, subKeywordDensity) => {
return total + (subKeywordDensity.density * 10);
}, 0);
const keywordDensityScore = this.keywordDensity * 10;
const totalScore = keywordInTitleScore + subKeywordsInTitleScore + subKeywordsDensityScore + keywordDensityScore;
return Math.min(totalScore, MAX_SCORE); // SEO score should never go above 100
}
getTitleWordCount() {
return this.htmlAnalyzer.getWordCount(this.content.title);
}
assignMessagesForKeyword() {
// warning for keyword not in content
if (this.content.keyword) {
goodPoints.push(`Your main keyword is "${this.content.keyword}".`);
this.messages.goodPoints.push(`Good, your content has a keyword "${this.content.keyword}".`);
// warning for keyword overstuffing
if (this.keywordDensity > 5) {
this.messages.warnings.push('Serious keyword overstuffing.');
}
// warning for keyword density too high or too low based on content length
if (this.keywordDensity < this.MINIMUM_KEYWORD_DENSITY) {
this.messages.warnings.push(`Keyword density is too low. It is ${this.keywordDensity.toFixed(2)}%, try increasing it.`);
}
else if (this.keywordDensity > this.MAXIMUM_KEYWORD_DENSITY) {
this.messages.warnings.push(`Keyword density is too high. It is ${this.keywordDensity.toFixed(2)}%, try decreasing it.`);
}
else {
this.messages.goodPoints.push(`Keyword density is ${this.keywordDensity.toFixed(2)}%.`);
}
}
else {
warnings.push('Missing main keyword.');
this.messages.warnings.push('Missing main keyword, please add one.');
}
}
assignMessagesForSubKeywords() {
// warning for sub keywords in content
if (this.content.subKeywords.length > 0) {
goodPoints.push(`Your sub keywords are "${this.content.subKeywords.join('", "')}".`);
this.messages.goodPoints.push(`Good, your content has sub keywords "${this.content.subKeywords.join(', ')}".`);
// warning for sub keywords not in title
const subKeywordsDensity = this.getSubKeywordsDensity();
subKeywordsDensity.forEach((subKeywordDensity) => {
if (subKeywordDensity.density > this.MAXIMUM_SUB_KEYWORD_DENSITY) {
this.messages.warnings.push(`The density of sub keyword "${subKeywordDensity.keyword}" is too high in the content, i.e. ${subKeywordDensity.density.toFixed(2)}%.`);
}
else if (subKeywordDensity.density < this.MINIMUM_SUB_KEYWORD_DENSITY) {
let densityBeingLowString = subKeywordDensity.density < this.EXTREME_LOW_SUB_KEYWORD_DENSITY ? 'too low' : 'low';
this.messages.warnings.push(`The density of sub keyword "${subKeywordDensity.keyword}" is ${densityBeingLowString} in the content, i.e. ${subKeywordDensity.density.toFixed(2)}%.`);
}
else {
this.messages.goodPoints.push(`The density of sub keyword "${subKeywordDensity.keyword}" is ${subKeywordDensity.density.toFixed(2)}% in the content, which is good.`);
}
});
}
else {
warnings.push('Missing sub keywords.');
this.messages.minorWarnings.push('Missing sub keywords, please add some.');
}
// warning for keyword density too high or too low based on content length
if (keywordDensity < this.MINIMUM_KEYWORD_DENSITY) {
warnings.push(`Keyword density is too low. It is ${keywordDensity.toFixed(2)}%, try increasing it.`);
}
else if (keywordDensity > this.MAXIMUM_KEYWORD_DENSITY) {
warnings.push(`Keyword density is too high. It is ${keywordDensity.toFixed(2)}%, try decreasing it.`);
}
else {
goodPoints.push(`Keyword density is ${keywordDensity.toFixed(2)}%.`);
}
// checking keyword density for subKeywords
const subKeywordsDensity = this.getSubKeywordsDensity();
subKeywordsDensity.forEach((subKeywordDensity) => {
if (subKeywordDensity.density > 3) {
warnings.push(`The density of sub keyword "${subKeywordDensity.keyword}" is too high, i.e. ${subKeywordDensity.density.toFixed(2)}%.`);
}
assignMessagesForTitle() {
// warning for content title and its length
if (this.content.title) {
if (this.content.title.length > this.MAXIMUM_TITLE_LENGTH) {
this.messages.warnings.push('Title tag is too long.');
}
else if (subKeywordDensity.density < 0.3) {
let densityBeingLowString = subKeywordDensity.density < 0.2 ? 'too low' : 'low';
warnings.push(`The density of sub keyword "${subKeywordDensity.keyword}" is ${densityBeingLowString}, i.e. ${subKeywordDensity.density.toFixed(2)}%.`);
else if (this.content.title.length < this.MINIMUM_TITLE_LENGTH) {
this.messages.warnings.push('Title tag is too short.');
}
else {
goodPoints.push(`The density of sub keyword "${subKeywordDensity.keyword}" is ${subKeywordDensity.density.toFixed(2)}%.`);
this.messages.goodPoints.push(`Title tag is ${this.content.title.length} characters long.`);
}
});
if (this.getKeywordInTitle()) {
goodPoints.push(`You have your main keyword in question.`);
const keywordInTitle = this.getKeywordInTitle();
if (keywordInTitle.density) {
this.messages.goodPoints.push(`Keyword density in title is ${keywordInTitle.density.toFixed(2)}%, which is good.`);
}
else {
this.messages.warnings.push('No main keyword in title.');
}
if (this.content.title) {
if (this.getSubKeywordsInTitle().length > 0) {
this.messages.goodPoints.push(`You have ${this.getSubKeywordsInTitle().length} sub keywords in title.`);
}
else {
this.messages.minorWarnings.push('No sub keywords in the title.');
}
}
}
else {
warnings.push('No main keyword in question.');
this.messages.warnings.push('Missing title tag, please add one.');
}
if (this.getSubKeywordsInTitle().length > 0) {
goodPoints.push(`You have ${this.getSubKeywordsInTitle().length} sub keywords in question.`);
}
else {
warnings.push('No sub keywords in question.');
}
}
assignMessagesForLinks() {
let wordCount = this.htmlAnalyzer.getWordCount();
// warning for less internal links based on content length
if (this.totalUniqueInternalLinksCount() < (wordCount / 100)) {
warnings.push(`Not enough internal links. You only have ${this.totalUniqueInternalLinksCount()} unique internal links, try increasing it.`);
if (this.totalUniqueInternalLinksCount() < (wordCount / 300)) {
this.messages.warnings.push(`Not enough internal links. You only have ${this.totalUniqueInternalLinksCount()} unique internal links, try increasing it.`);
}
else {
goodPoints.push(`You have ${this.totalUniqueInternalLinksCount()} internal links.`);
this.messages.goodPoints.push(`You have ${this.totalUniqueInternalLinksCount()} internal links.`);
}
// warning for less outbound links based on content length
if (this.totalUniqueExternalLinksCount() < (wordCount / 200)) {
warnings.push(`Not enough outbound links. You only have ${this.totalUniqueExternalLinksCount()}, try increasing it.`);
if (this.totalUniqueExternalLinksCount() < (wordCount / 400)) {
this.messages.warnings.push(`Not enough outbound links. You only have ${this.totalUniqueExternalLinksCount()}, try increasing it.`);
}
// warning for duplicate internal links
if (this.htmlAnalyzer.getInternalLinks().duplicate.length > 1) {
warnings.push(`You have ${this.htmlAnalyzer.getInternalLinks().duplicate.length} duplicate internal links.`);
this.messages.minorWarnings.push(`You have ${this.htmlAnalyzer.getInternalLinks().duplicate.length} duplicate internal links.`);
}
else {
goodPoints.push('No duplicate internal links.');
this.messages.goodPoints.push('No duplicate internal links.');
}
// warning for duplicate external links
if (this.htmlAnalyzer.getOutboundLinks().duplicate.length > 1) {
warnings.push(`You have ${this.htmlAnalyzer.getOutboundLinks().duplicate.length} duplicate outbound links.`);
this.messages.minorWarnings.push(`You have ${this.htmlAnalyzer.getOutboundLinks().duplicate.length} duplicate outbound links.`);
}
else {
goodPoints.push('No duplicate outbound links.');
this.messages.goodPoints.push('No duplicate outbound links.');
}
if (this.getKeywordDensity() > 10)
warnings.push('Serious keyword overstuffing.');
if (this.htmlDom('title').text().length > 60)
warnings.push('Title tag is too long.');
if (!this.content.metaDescription)
warnings.push('Missing meta description.');
return { warnings, goodPoints };
}
getKeywordInTitle(keyword = null) {
var _a;
if (keyword === null) {
keyword = this.content.keyword;
assignMessagesForMetaDescription() {
if (this.content.metaDescription) {
let keywordInMetaDescription = this.getKeywordInMetaDescription();
// warning for meta description length
if (this.content.metaDescription.length > this.MAXIMUM_META_DESCRIPTION_LENGTH) {
this.messages.warnings.push(`Meta description is too long. It is ${this.content.metaDescription.length} characters long, try reducing it.`);
}
else if (this.content.metaDescription.length < 100) {
this.messages.warnings.push(`Meta description is too short. It is ${this.content.metaDescription.length} characters long, try increasing it.`);
}
else {
this.messages.goodPoints.push(`Meta description is ${this.content.metaDescription.length} characters long.`);
// warning for meta description keyword density
if (keywordInMetaDescription.density > this.MAXIMUM_META_DESCRIPTION_DENSITY) {
this.messages.warnings.push(`Keyword density of meta description is too high. It is ${keywordInMetaDescription.density.toFixed(2)}%, try decreasing it.`);
}
else if (keywordInMetaDescription.density < this.MINIMUM_META_DESCRIPTION_DENSITY) {
this.messages.warnings.push(`Keyword density of meta description is too low. It is ${keywordInMetaDescription.density.toFixed(2)}%, try increasing it.`);
}
else {
this.messages.goodPoints.push(`Keyword density of meta description is ${keywordInMetaDescription.density.toFixed(2)}%, which is good.`);
}
}
// warning for meta description not starting with keyword
if (keywordInMetaDescription.position > 1) {
this.messages.minorWarnings.push(`Meta description does not start with keyword. It starts with "${this.content.metaDescription.substring(0, 20)}", try starting with keyword. Not starting with keyword is not a big issue, but it is recommended to start with keyword.`);
}
else {
this.messages.goodPoints.push(`Meta description starts with keyword, i.e. "${this.content.metaDescription.substring(0, 20)}".`);
}
// warning for meta description not ending with keyword
let subKeywordsInMetaDescription = this.getSubKeywordsInMetaDescription();
subKeywordsInMetaDescription.forEach((subKeyword) => {
if (subKeyword.density > this.MAXIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY) {
this.messages.warnings.push(`The density of sub keyword "${subKeyword.keyword}" in meta description is too high, i.e. ${subKeyword.density.toFixed(2)}%.`);
}
else if (subKeyword.density < this.MINIMUM_SUB_KEYWORD_IN_META_DESCRIPTION_DENSITY) {
let densityBeingLowString = subKeyword.density < 0.2 ? 'too low' : 'low';
this.messages.warnings.push(`The density of sub keyword "${subKeyword.keyword}" in meta description is ${densityBeingLowString}, i.e. ${subKeyword.density.toFixed(2)}%.`);
}
else {
this.messages.goodPoints.push(`The density of sub keyword "${subKeyword.keyword}" in meta description is ${subKeyword.density.toFixed(2)}%.`);
}
});
}
const density = this.calculateDensity(keyword, this.content.title);
return {
keyword,
density,
position: (_a = this.content.title) === null || _a === void 0 ? void 0 : _a.indexOf(keyword)
};
else {
this.messages.warnings.push('Missing meta description.');
}
}
getSubKeywordsInTitle() {
let subKeywordsInQuestion = [];
this.content.subKeywords.forEach((sub_keyword) => {
subKeywordsInQuestion.push(this.getKeywordInTitle(sub_keyword));
});
return subKeywordsInQuestion;
/**
* Returns the messages object.
* @return object The messages object.
* @example
* {
* goodPoints: [],
* warnings: [],
* minorWarnings: [],
* }
* @see SeoAnalyzer.messages
*/
assignMessages() {
this.assignMessagesForKeyword();
this.assignMessagesForSubKeywords();
this.assignMessagesForTitle();
this.assignMessagesForLinks();
this.assignMessagesForMetaDescription();
return this.messages;
}
countOccurrencesInString(keyword, stringContent) {
return stringContent.split(keyword).length - 1;
/**
* Calculates the density of a keyword in the given string of body text.
* @param keyword Should not be null.
* @param bodyText If null, it will use the default value, i.e. `this.htmlAnalyzer.bodyText`
*/
calculateDensity(keyword, bodyText = null) {
bodyText = bodyText !== null && bodyText !== void 0 ? bodyText : this.htmlAnalyzer.bodyText;
return (this.countOccurrencesInString(keyword, bodyText) / this.htmlAnalyzer.getWordCount(bodyText)) * 100;
}
getSeoScore() {
const MAX_SCORE = 100;
const { warnings, goodPoints } = this.getMessages();
const messagesScore = ((goodPoints.length) / (warnings.length + goodPoints.length)) * 100;
return Math.min(messagesScore, MAX_SCORE); // SEO score should never go above 100
/**
* Returns the number of occurrences of a keyword in a string. Or you can say, it returns the keyword count in the given string.
* @param keyword If null, it will use the default value, i.e. `this.content.keyword`
* @param stringContent If null, it will use the default value, i.e. `this.htmlAnalyzer.bodyText`
* @return number The number of occurrences of the keyword in the string.
*/
countOccurrencesInString(keyword = null, stringContent = null) {
keyword = keyword !== null && keyword !== void 0 ? keyword : this.content.keyword;
stringContent = stringContent !== null && stringContent !== void 0 ? stringContent : this.htmlAnalyzer.bodyText;
return stringContent.split(keyword).length - 1; // -1 because the split function will always return one more than the actual occurrences
}
getKeywordSeoScore() {
const MAX_SCORE = 100;
const keywordDensity = this.getKeywordDensity();
const keywordInQuestion = this.getKeywordInTitle();
const subKeywordsInQuestion = this.getSubKeywordsInTitle();
const subKeywordsDensity = this.getSubKeywordsDensity();
const keywordInQuestionScore = keywordInQuestion.density * 10;
const subKeywordsInQuestionScore = subKeywordsInQuestion.length * 10;
const subKeywordsDensityScore = subKeywordsDensity.reduce((total, subKeywordDensity) => {
return total + (subKeywordDensity.density * 10);
}, 0);
const keywordDensityScore = keywordDensity * 10;
const totalScore = keywordInQuestionScore + subKeywordsInQuestionScore + subKeywordsDensityScore + keywordDensityScore;
return Math.min(totalScore, MAX_SCORE); // SEO score should never go above 100
}
getTitleWordCount() {
return this.htmlAnalyzer.getWordCount(this.content.title);
}
}
exports.SeoAnalyzer = SeoAnalyzer;
//# sourceMappingURL=seo-analyzer.js.map

@@ -10,7 +10,11 @@ import { SeoAnalyzer } from './seo-analyzer';

constructor(contentJson: ContentJson, siteDomainName?: string | null);
private makeContentLowerCase;
analyzeSeo(): {
seoScore: number;
wordCount: number;
keywordSeoScore: number;
keywordFrequency: number;
messages: {
warnings: string[];
minorWarnings: string[];
goodPoints: string[];

@@ -26,4 +30,5 @@ };

keywordWithTitle: import("./interfaces").KeywordDensity;
wordCount: number;
};
};
}

@@ -9,2 +9,3 @@ "use strict";

this.content = contentJson;
this.makeContentLowerCase();
this.siteDomainName = siteDomainName;

@@ -14,8 +15,18 @@ this.htmlAnalyzer = new html_analyzer_1.HtmlAnalyzer(this.content.htmlText, this.siteDomainName);

}
makeContentLowerCase() {
this.content.title = this.content.title.toLowerCase();
this.content.metaDescription = this.content.metaDescription.toLowerCase();
this.content.keyword = this.content.keyword.toLowerCase();
this.content.subKeywords = this.content.subKeywords.map((subKeyword) => {
return subKeyword.toLowerCase();
});
}
analyzeSeo() {
return {
seoScore: this.seoAnalyzer.getSeoScore(),
wordCount: this.htmlAnalyzer.getWordCount(),
keywordSeoScore: this.seoAnalyzer.getKeywordSeoScore(),
messages: this.seoAnalyzer.getMessages(),
keywordDensity: this.seoAnalyzer.getKeywordDensity(),
keywordFrequency: this.seoAnalyzer.countOccurrencesInString(),
messages: this.seoAnalyzer.messages,
keywordDensity: this.seoAnalyzer.keywordDensity,
subKeywordDensity: this.seoAnalyzer.getSubKeywordsDensity(),

@@ -28,2 +39,3 @@ totalLinks: this.htmlAnalyzer.getAllLinks().length,

keywordWithTitle: this.seoAnalyzer.getKeywordInTitle(),
wordCount: this.seoAnalyzer.getTitleWordCount(),
}

@@ -30,0 +42,0 @@ };

{
"name": "seord",
"version": "0.0.2",
"version": "0.0.3",
"description": "Advanced SEO Analyzer",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

# SEO Analyzer - SEOrd
SEOrd `(pronounced "sword")` is an advanced SEO Analyzer library that allows you to perform an SEO analysis
on HTML content. SEOrd helps check the SEO friendliness of a website by looking at different factors such
SEOrd `(pronounced "sword")` is an advanced content SEO Analyzer library that allows you to perform a rapid SEO analysis
on your rich text-ed content. SEOrd helps check the SEO friendliness of a website by looking at different factors such
as keyword density, meta description, and link analysis `(internal and external link)`.

@@ -10,2 +10,6 @@

> **Note:** This library analyzes the SEO of the given content on the basis of the standard on-page SEO rules.
> This library is just a tool to help you improve your SEO score or use it as a reference or a supplement to
> your webapps.
## Install

@@ -19,3 +23,3 @@

- Keyword Density Analysis
- Keyword Density and Frequency Analysis
- Sub Keywords Density Analysis

@@ -27,3 +31,5 @@ - SEO Messages: Get warnings and good points related to SEO.

- SEO Score Analysis
- Key SEO Score Analysis
- Keyword SEO Score
- Word Count Analysis
- Checks for meta description length, placement, keyword density, and keyword frequency

@@ -43,41 +49,45 @@ ## Usage

import {SeoCheck} from "seord";
import {readFile} from 'fs'; // Only for this example
const htmlContent = `<h1>Does Progressive Raise Your Rates After 6 Months?</h1><p>When it comes to car insurance,
finding the right provider...
`
const contentJson = {
title: 'Does Progressive raise your rates after 6 months?',
htmlText: htmlContent,
keyword: 'progressive',
subKeywords: ['car insurance', 'rates', 'premiums', 'save money', 'US'],
metaDescription: 'Find out if Progressive raises your rates after 6 months and what factors can impact your insurance premiums. Learn how to save money on car insurance in the US.',
languageCode: 'en',
countryCode: 'us'
};
readFile('test.html', 'utf8' , (err, htmlContent) => {
if (err) {
console.error(err)
return
}
// Initialize SeoCheck with html content, main keyword and sub keywords
const seoCheck = new SeoCheck(contentJson, 'liveinabroad.com');
// Initialize SeoCheck with html content, main keyword and sub keywords
const seoCheck = new SeoCheck(
{
question: 'What\'s the best insurance cover to get?',
htmlText: htmlContent,
keyword: 'best insurance cover',
subKeywords: ['types of insurance', 'insurance coverage', 'insurance options'],
metaDescription: 'Discover the best insurance cover to protect yourself and your loved ones. Explore different types of insurance and find the right coverage for your needs.',
languageCode: 'en',
countryCode: 'us'
},
'liveinabroad.com'
);
// Perform analysis
const result = seoCheck.analyzeSeo();
// Print the result
console.log(`Warnings: ${result.messages.warnings.length}`);
result.messages.warnings.forEach((warning) => {
console.log(` - ${warning}`);
});
console.log(`\nGood Points: ${result.messages.goodPoints.length}`);
result.messages.goodPoints.forEach((error) => {
console.log(` - ${error}`);
});
// Perform analysis
const result = seoCheck.analyzeSeo();
// Print the result
console.log("Warnings: " + result.messages.warnings.length);
result.messages.warnings.forEach((warning) => {
console.log(' - ' + warning);
});
console.log(`\nMinor Warnings: ${result.messages.minorWarnings.length}`);
result.messages.minorWarnings.forEach((error) => {
console.log(` - ${error}`);
});
console.log("\nGood Points: " + result.messages.goodPoints.length);
result.messages.goodPoints.forEach((error) => {
console.log(' - ' + error);
});
console.log("\nSEO Score: " + result.seoScore);
console.log("Keyword SEO Score: " + result.keywordSeoScore);
})
console.log("\nSEO Score: " + result.seoScore);
console.log(`Keyword SEO Score: ${result.keywordSeoScore}`);
console.log(`Keyword Density: ${result.keywordDensity}`);
console.log(`Sub Keyword Density: ${result.subKeywordDensity.map((subKeywordDensity) => {
return `(${subKeywordDensity.keyword} ${subKeywordDensity.density})`;
})}`);
console.log(`Keyword Frequency: ${result.keywordFrequency}`);
console.log(`Word Count: ${result.wordCount}`);
console.log(`Total Links: ${result.totalLinks}`);
```

@@ -91,20 +101,37 @@

```text
Warnings: 3
- The density of sub keyword "insurance options" is too low, i.e. 0.08%.
- Not enough internal links. You only have 0 unique internal links, try increasing it.
Warnings: 7
- The density of sub keyword "car insurance" is too high in the content, i.e. 2.34%.
- The density of sub keyword "rates" is too high in the content, i.e. 4.28%.
- The density of sub keyword "premiums" is too high in the content, i.e. 1.38%.
- The density of sub keyword "us" is too high in the content, i.e. 1.38%.
- Not enough internal links. You only have 1 unique internal links, try increasing it.
- Not enough outbound links. You only have 0, try increasing it.
- Meta description is too long. It is 161 characters long, try reducing it.
Good Points: 9
- Your main keyword is "best insurance cover".
- Your sub keywords are "types of insurance", "insurance coverage", "insurance options".
- Keyword density is 0.62%.
- The density of sub keyword "types of insurance" is 0.47%.
- The density of sub keyword "insurance coverage" is 0.62%.
- You have your main keyword in question.
- You have 3 sub keywords in question.
Good Points: 14
- Good, your content has a keyword "progressive".
- Keyword density is 1.52%.
- Good, your content has sub keywords "car insurance, rates, premiums, save money, us".
- The density of sub keyword "save money" is 0.55% in the content, which is good.
- Title tag is 49 characters long.
- Keyword density in title is 12.50%, which is good.
- You have 5 sub keywords in title.
- No duplicate internal links.
- No duplicate outbound links.
- The density of sub keyword "car insurance" in meta description is 3.45%.
- The density of sub keyword "rates" in meta description is 3.45%.
- The density of sub keyword "premiums" in meta description is 3.45%.
- The density of sub keyword "save money" in meta description is 3.45%.
- The density of sub keyword "us" in meta description is 3.45%.
SEO Score: 75
Minor Warnings: 1
- Meta description does not start with keyword. It starts with "find out if progress", try starting with keyword. Not starting with keyword is not a big issue, but it is recommended to start with keyword.
SEO Score: 66.66666666666666
Keyword SEO Score: 100
Keyword Density: 1.5172413793103448
Sub Keyword Density: (car insurance 2.344827586206897),(rates 4.275862068965517),(premiums 1.3793103448275863),(save money 0.5517241379310345),(us 1.3793103448275863)
Keyword Frequency: 11
Word Count: 725
Total Links: 1
```

@@ -119,4 +146,6 @@

seoScore: number,
wordCount: number,
keywordSeoScore: number,
messages: { warnings: string[], goodPoints: string[] },
keywordFrequency: number,
messages: { warnings: string[], goodPoints: string[], minorWarnings: string[] },
keywordDensity: number,

@@ -127,5 +156,6 @@ subKeywordDensity: KeywordDensity[],

outboundLinks: LinksGroup,
questionSEO: {
subKeywordsWithQuestion: KeywordDensity[],
keywordWithQuestion: KeywordDensity
titleSEO: {
subKeywordsWithTitle: KeywordDensity[],
keywordWithTitle: KeywordDensity,
wordCount: number
}

@@ -146,2 +176,9 @@ }

This project is licensed under the MIT license. Please see the [LICENSE](LICENSE) file for more information.
This project is licensed under the MIT license. Please see the [LICENSE](LICENSE) file for more information.
> **Note:** This library is still in development. I will be adding more features in the future.
> If you have any suggestions, please let me know.
Please note that this library is not affiliated with Google or any other search engine.
> It does not guarantee the SEO score calculated by this library will be the same as the SEO score
> calculated by Google or any other search engine.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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