You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@power-seo/content-analysis

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@power-seo/content-analysis - npm Package Compare versions

Comparing version
1.0.6
to
1.0.7
+94
-32
dist/index.cjs

@@ -1,6 +0,36 @@

'use strict';
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var core = require('@power-seo/core');
// src/index.ts
var src_exports = {};
__export(src_exports, {
analyzeContent: () => analyzeContent,
checkHeadings: () => checkHeadings,
checkImages: () => checkImages,
checkKeyphraseUsage: () => checkKeyphraseUsage,
checkLinks: () => checkLinks,
checkMetaDescription: () => checkMetaDescription,
checkTitle: () => checkTitle,
checkWordCount: () => checkWordCount
});
module.exports = __toCommonJS(src_exports);
// src/checks/title.ts
var import_core = require("@power-seo/core");
function checkTitle(input) {

@@ -20,3 +50,3 @@ const results = [];

}
const validation = core.validateTitle(title);
const validation = (0, import_core.validateTitle)(title);
if (!validation.valid) {

@@ -75,2 +105,5 @@ results.push({

}
// src/checks/meta-description.ts
var import_core2 = require("@power-seo/core");
function checkMetaDescription(input) {

@@ -90,3 +123,3 @@ const results = [];

}
const validation = core.validateMetaDescription(metaDescription);
const validation = (0, import_core2.validateMetaDescription)(metaDescription);
if (!validation.valid) {

@@ -145,2 +178,5 @@ results.push({

}
// src/checks/keyphrase-usage.ts
var import_core3 = require("@power-seo/core");
function checkKeyphraseUsage(input) {

@@ -160,3 +196,3 @@ const results = [];

}
const occurrences = core.analyzeKeyphraseOccurrences({
const occurrences = (0, import_core3.analyzeKeyphraseOccurrences)({
keyphrase: focusKeyphrase,

@@ -169,8 +205,8 @@ title,

});
const densityResult = core.calculateKeywordDensity(focusKeyphrase, content);
if (densityResult.density < core.KEYWORD_DENSITY.MIN) {
const densityResult = (0, import_core3.calculateKeywordDensity)(focusKeyphrase, content);
if (densityResult.density < import_core3.KEYWORD_DENSITY.MIN) {
results.push({
id: "keyphrase-density",
title: "Keyphrase density",
description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${core.KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,
description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${import_core3.KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,
status: "poor",

@@ -180,7 +216,7 @@ score: 1,

});
} else if (densityResult.density > core.KEYWORD_DENSITY.MAX) {
} else if (densityResult.density > import_core3.KEYWORD_DENSITY.MAX) {
results.push({
id: "keyphrase-density",
title: "Keyphrase density",
description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${core.KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,
description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${import_core3.KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,
status: "poor",

@@ -190,4 +226,4 @@ score: 1,

});
} else if (densityResult.density >= core.KEYWORD_DENSITY.MIN && densityResult.density <= core.KEYWORD_DENSITY.MAX) {
const isOptimal = Math.abs(densityResult.density - core.KEYWORD_DENSITY.OPTIMAL) < 0.5;
} else if (densityResult.density >= import_core3.KEYWORD_DENSITY.MIN && densityResult.density <= import_core3.KEYWORD_DENSITY.MAX) {
const isOptimal = Math.abs(densityResult.density - import_core3.KEYWORD_DENSITY.OPTIMAL) < 0.5;
results.push({

@@ -238,11 +274,33 @@ id: "keyphrase-density",

}
// src/checks/headings.ts
var import_core4 = require("@power-seo/core");
function parseHeadings(html) {
const headings = [];
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
const lc = html.toLowerCase();
let pos = 0;
while (pos < lc.length) {
let earliest = -1;
let earliestLevel = 0;
for (let level = 1; level <= 6; level++) {
const idx = lc.indexOf(`<h${level}`, pos);
if (idx !== -1 && (earliest === -1 || idx < earliest)) {
earliest = idx;
earliestLevel = level;
}
}
if (earliest === -1) break;
const contentStart = lc.indexOf(">", earliest);
if (contentStart === -1) break;
const closeTag = `</h${earliestLevel}>`;
const closeIdx = lc.indexOf(closeTag, contentStart + 1);
if (closeIdx === -1) {
pos = contentStart + 1;
continue;
}
headings.push({
level: parseInt(match[1], 10),
text: core.stripHtml(match[2])
level: earliestLevel,
text: (0, import_core4.stripHtml)(html.slice(contentStart + 1, closeIdx))
});
pos = closeIdx + closeTag.length;
}

@@ -339,10 +397,13 @@ return headings;

}
// src/checks/word-count.ts
var import_core5 = require("@power-seo/core");
function checkWordCount(input) {
const words = core.getWords(input.content);
const words = (0, import_core5.getWords)(input.content);
const count = words.length;
if (count < core.MIN_WORD_COUNT) {
if (count < import_core5.MIN_WORD_COUNT) {
return {
id: "word-count",
title: "Word count",
description: `The content is ${count} words, which is below the recommended minimum of ${core.MIN_WORD_COUNT}. Add more content to improve SEO.`,
description: `The content is ${count} words, which is below the recommended minimum of ${import_core5.MIN_WORD_COUNT}. Add more content to improve SEO.`,
status: "poor",

@@ -353,7 +414,7 @@ score: 1,

}
if (count < core.RECOMMENDED_WORD_COUNT) {
if (count < import_core5.RECOMMENDED_WORD_COUNT) {
return {
id: "word-count",
title: "Word count",
description: `The content is ${count} words. Consider expanding to at least ${core.RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,
description: `The content is ${count} words. Consider expanding to at least ${import_core5.RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,
status: "ok",

@@ -526,12 +587,13 @@ score: 3,

}
exports.analyzeContent = analyzeContent;
exports.checkHeadings = checkHeadings;
exports.checkImages = checkImages;
exports.checkKeyphraseUsage = checkKeyphraseUsage;
exports.checkLinks = checkLinks;
exports.checkMetaDescription = checkMetaDescription;
exports.checkTitle = checkTitle;
exports.checkWordCount = checkWordCount;
//# sourceMappingURL=index.cjs.map
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
analyzeContent,
checkHeadings,
checkImages,
checkKeyphraseUsage,
checkLinks,
checkMetaDescription,
checkTitle,
checkWordCount
});
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"names":["validateTitle","validateMetaDescription","analyzeKeyphraseOccurrences","calculateKeywordDensity","KEYWORD_DENSITY","stripHtml","getWords","MIN_WORD_COUNT","RECOMMENDED_WORD_COUNT"],"mappings":"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","file":"index.cjs","sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const regex = /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi;\n let match;\n while ((match = regex.exec(html)) !== null) {\n headings.push({\n level: parseInt(match[1]!, 10),\n text: stripHtml(match[2]!),\n });\n }\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"]}
{"version":3,"sources":["../src/index.ts","../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Public API\n// ============================================================================\n\nexport { analyzeContent } from './analyzer.js';\nexport { checkTitle } from './checks/title.js';\nexport { checkMetaDescription } from './checks/meta-description.js';\nexport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nexport { checkHeadings } from './checks/headings.js';\nexport { checkWordCount } from './checks/word-count.js';\nexport { checkImages } from './checks/images.js';\nexport { checkLinks } from './checks/links.js';\n\nexport type {\n CheckId,\n AnalysisConfig,\n ContentAnalysisInput,\n ContentAnalysisOutput,\n AnalysisResult,\n AnalysisStatus,\n} from './types.js';\n","// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\n/**\n * Parse all headings (h1-h6) from HTML using plain string search.\n * Avoids regex ReDoS on crafted inputs with many repeated heading-like tags.\n */\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const lc = html.toLowerCase();\n let pos = 0;\n\n while (pos < lc.length) {\n // Find the next heading tag of any level\n let earliest = -1;\n let earliestLevel = 0;\n for (let level = 1; level <= 6; level++) {\n const idx = lc.indexOf(`<h${level}`, pos);\n if (idx !== -1 && (earliest === -1 || idx < earliest)) {\n earliest = idx;\n earliestLevel = level;\n }\n }\n if (earliest === -1) break;\n\n const contentStart = lc.indexOf('>', earliest);\n if (contentStart === -1) break;\n\n const closeTag = `</h${earliestLevel}>`;\n const closeIdx = lc.indexOf(closeTag, contentStart + 1);\n if (closeIdx === -1) {\n pos = contentStart + 1;\n continue;\n }\n\n headings.push({\n level: earliestLevel,\n text: stripHtml(html.slice(contentStart + 1, closeIdx)),\n });\n pos = closeIdx + closeTag.length;\n }\n\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core"]}

@@ -1,4 +0,3 @@

import { validateTitle, validateMetaDescription, analyzeKeyphraseOccurrences, calculateKeywordDensity, KEYWORD_DENSITY, getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT, stripHtml } from '@power-seo/core';
// src/checks/title.ts
import { validateTitle } from "@power-seo/core";
function checkTitle(input) {

@@ -72,2 +71,5 @@ const results = [];

}
// src/checks/meta-description.ts
import { validateMetaDescription } from "@power-seo/core";
function checkMetaDescription(input) {

@@ -141,2 +143,9 @@ const results = [];

}
// src/checks/keyphrase-usage.ts
import {
analyzeKeyphraseOccurrences,
calculateKeywordDensity,
KEYWORD_DENSITY
} from "@power-seo/core";
function checkKeyphraseUsage(input) {

@@ -230,11 +239,33 @@ const results = [];

}
// src/checks/headings.ts
import { stripHtml } from "@power-seo/core";
function parseHeadings(html) {
const headings = [];
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
const lc = html.toLowerCase();
let pos = 0;
while (pos < lc.length) {
let earliest = -1;
let earliestLevel = 0;
for (let level = 1; level <= 6; level++) {
const idx = lc.indexOf(`<h${level}`, pos);
if (idx !== -1 && (earliest === -1 || idx < earliest)) {
earliest = idx;
earliestLevel = level;
}
}
if (earliest === -1) break;
const contentStart = lc.indexOf(">", earliest);
if (contentStart === -1) break;
const closeTag = `</h${earliestLevel}>`;
const closeIdx = lc.indexOf(closeTag, contentStart + 1);
if (closeIdx === -1) {
pos = contentStart + 1;
continue;
}
headings.push({
level: parseInt(match[1], 10),
text: stripHtml(match[2])
level: earliestLevel,
text: stripHtml(html.slice(contentStart + 1, closeIdx))
});
pos = closeIdx + closeTag.length;
}

@@ -331,2 +362,5 @@ return headings;

}
// src/checks/word-count.ts
import { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from "@power-seo/core";
function checkWordCount(input) {

@@ -517,5 +551,12 @@ const words = getWords(input.content);

}
export { analyzeContent, checkHeadings, checkImages, checkKeyphraseUsage, checkLinks, checkMetaDescription, checkTitle, checkWordCount };
//# sourceMappingURL=index.js.map
export {
analyzeContent,
checkHeadings,
checkImages,
checkKeyphraseUsage,
checkLinks,
checkMetaDescription,
checkTitle,
checkWordCount
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const regex = /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi;\n let match;\n while ((match = regex.exec(html)) !== null) {\n headings.push({\n level: parseInt(match[1]!, 10),\n text: stripHtml(match[2]!),\n });\n }\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"]}
{"version":3,"sources":["../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\n/**\n * Parse all headings (h1-h6) from HTML using plain string search.\n * Avoids regex ReDoS on crafted inputs with many repeated heading-like tags.\n */\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const lc = html.toLowerCase();\n let pos = 0;\n\n while (pos < lc.length) {\n // Find the next heading tag of any level\n let earliest = -1;\n let earliestLevel = 0;\n for (let level = 1; level <= 6; level++) {\n const idx = lc.indexOf(`<h${level}`, pos);\n if (idx !== -1 && (earliest === -1 || idx < earliest)) {\n earliest = idx;\n earliestLevel = level;\n }\n }\n if (earliest === -1) break;\n\n const contentStart = lc.indexOf('>', earliest);\n if (contentStart === -1) break;\n\n const closeTag = `</h${earliestLevel}>`;\n const closeIdx = lc.indexOf(closeTag, contentStart + 1);\n if (closeIdx === -1) {\n pos = contentStart + 1;\n continue;\n }\n\n headings.push({\n level: earliestLevel,\n text: stripHtml(html.slice(contentStart + 1, closeIdx)),\n });\n pos = closeIdx + closeTag.length;\n }\n\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"],"mappings":"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","names":[]}

@@ -1,7 +0,32 @@

'use strict';
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var react = require('react');
var core = require('@power-seo/core');
// src/react.ts
var react_exports = {};
__export(react_exports, {
CheckList: () => CheckList,
ContentAnalyzer: () => ContentAnalyzer,
ScorePanel: () => ScorePanel
});
module.exports = __toCommonJS(react_exports);
var import_react = require("react");
// src/react.ts
// src/checks/title.ts
var import_core = require("@power-seo/core");
function checkTitle(input) {

@@ -21,3 +46,3 @@ const results = [];

}
const validation = core.validateTitle(title);
const validation = (0, import_core.validateTitle)(title);
if (!validation.valid) {

@@ -76,2 +101,5 @@ results.push({

}
// src/checks/meta-description.ts
var import_core2 = require("@power-seo/core");
function checkMetaDescription(input) {

@@ -91,3 +119,3 @@ const results = [];

}
const validation = core.validateMetaDescription(metaDescription);
const validation = (0, import_core2.validateMetaDescription)(metaDescription);
if (!validation.valid) {

@@ -146,2 +174,5 @@ results.push({

}
// src/checks/keyphrase-usage.ts
var import_core3 = require("@power-seo/core");
function checkKeyphraseUsage(input) {

@@ -161,3 +192,3 @@ const results = [];

}
const occurrences = core.analyzeKeyphraseOccurrences({
const occurrences = (0, import_core3.analyzeKeyphraseOccurrences)({
keyphrase: focusKeyphrase,

@@ -170,8 +201,8 @@ title,

});
const densityResult = core.calculateKeywordDensity(focusKeyphrase, content);
if (densityResult.density < core.KEYWORD_DENSITY.MIN) {
const densityResult = (0, import_core3.calculateKeywordDensity)(focusKeyphrase, content);
if (densityResult.density < import_core3.KEYWORD_DENSITY.MIN) {
results.push({
id: "keyphrase-density",
title: "Keyphrase density",
description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${core.KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,
description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${import_core3.KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,
status: "poor",

@@ -181,7 +212,7 @@ score: 1,

});
} else if (densityResult.density > core.KEYWORD_DENSITY.MAX) {
} else if (densityResult.density > import_core3.KEYWORD_DENSITY.MAX) {
results.push({
id: "keyphrase-density",
title: "Keyphrase density",
description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${core.KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,
description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${import_core3.KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,
status: "poor",

@@ -191,4 +222,4 @@ score: 1,

});
} else if (densityResult.density >= core.KEYWORD_DENSITY.MIN && densityResult.density <= core.KEYWORD_DENSITY.MAX) {
const isOptimal = Math.abs(densityResult.density - core.KEYWORD_DENSITY.OPTIMAL) < 0.5;
} else if (densityResult.density >= import_core3.KEYWORD_DENSITY.MIN && densityResult.density <= import_core3.KEYWORD_DENSITY.MAX) {
const isOptimal = Math.abs(densityResult.density - import_core3.KEYWORD_DENSITY.OPTIMAL) < 0.5;
results.push({

@@ -239,11 +270,33 @@ id: "keyphrase-density",

}
// src/checks/headings.ts
var import_core4 = require("@power-seo/core");
function parseHeadings(html) {
const headings = [];
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
const lc = html.toLowerCase();
let pos = 0;
while (pos < lc.length) {
let earliest = -1;
let earliestLevel = 0;
for (let level = 1; level <= 6; level++) {
const idx = lc.indexOf(`<h${level}`, pos);
if (idx !== -1 && (earliest === -1 || idx < earliest)) {
earliest = idx;
earliestLevel = level;
}
}
if (earliest === -1) break;
const contentStart = lc.indexOf(">", earliest);
if (contentStart === -1) break;
const closeTag = `</h${earliestLevel}>`;
const closeIdx = lc.indexOf(closeTag, contentStart + 1);
if (closeIdx === -1) {
pos = contentStart + 1;
continue;
}
headings.push({
level: parseInt(match[1], 10),
text: core.stripHtml(match[2])
level: earliestLevel,
text: (0, import_core4.stripHtml)(html.slice(contentStart + 1, closeIdx))
});
pos = closeIdx + closeTag.length;
}

@@ -340,10 +393,13 @@ return headings;

}
// src/checks/word-count.ts
var import_core5 = require("@power-seo/core");
function checkWordCount(input) {
const words = core.getWords(input.content);
const words = (0, import_core5.getWords)(input.content);
const count = words.length;
if (count < core.MIN_WORD_COUNT) {
if (count < import_core5.MIN_WORD_COUNT) {
return {
id: "word-count",
title: "Word count",
description: `The content is ${count} words, which is below the recommended minimum of ${core.MIN_WORD_COUNT}. Add more content to improve SEO.`,
description: `The content is ${count} words, which is below the recommended minimum of ${import_core5.MIN_WORD_COUNT}. Add more content to improve SEO.`,
status: "poor",

@@ -354,7 +410,7 @@ score: 1,

}
if (count < core.RECOMMENDED_WORD_COUNT) {
if (count < import_core5.RECOMMENDED_WORD_COUNT) {
return {
id: "word-count",
title: "Word count",
description: `The content is ${count} words. Consider expanding to at least ${core.RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,
description: `The content is ${count} words. Consider expanding to at least ${import_core5.RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,
status: "ok",

@@ -543,3 +599,3 @@ score: 3,

const label = getScoreLabel(percentage);
return react.createElement(
return (0, import_react.createElement)(
"div",

@@ -555,3 +611,3 @@ {

},
react.createElement(
(0, import_react.createElement)(
"div",

@@ -566,3 +622,3 @@ {

},
react.createElement(
(0, import_react.createElement)(
"span",

@@ -572,3 +628,3 @@ { style: { fontWeight: 600, fontSize: "14px", color: "#333" } },

),
react.createElement(
(0, import_react.createElement)(
"span",

@@ -579,3 +635,3 @@ { style: { fontWeight: 700, fontSize: "18px", color } },

),
react.createElement(
(0, import_react.createElement)(
"div",

@@ -591,3 +647,3 @@ {

},
react.createElement("div", {
(0, import_react.createElement)("div", {
style: {

@@ -602,3 +658,3 @@ width: `${percentage}%`,

),
react.createElement(
(0, import_react.createElement)(
"div",

@@ -621,3 +677,3 @@ { style: { marginTop: "4px", fontSize: "12px", color: "#666" } },

function CheckList({ results }) {
return react.createElement(
return (0, import_react.createElement)(
"ul",

@@ -633,3 +689,3 @@ {

...results.map(
(result) => react.createElement(
(result) => (0, import_react.createElement)(
"li",

@@ -646,3 +702,3 @@ {

},
react.createElement(
(0, import_react.createElement)(
"span",

@@ -652,6 +708,6 @@ { style: { flexShrink: 0, fontSize: "14px" } },

),
react.createElement(
(0, import_react.createElement)(
"div",
{ style: { flex: 1 } },
react.createElement(
(0, import_react.createElement)(
"div",

@@ -667,3 +723,3 @@ {

),
react.createElement(
(0, import_react.createElement)(
"div",

@@ -679,4 +735,4 @@ { style: { fontSize: "12px", color: "#555", marginTop: "2px" } },

function ContentAnalyzer({ input, config, children }) {
const output = react.useMemo(() => analyzeContent(input, config), [input, config]);
return react.createElement(
const output = (0, import_react.useMemo)(() => analyzeContent(input, config), [input, config]);
return (0, import_react.createElement)(
"div",

@@ -688,13 +744,14 @@ {

},
react.createElement(ScorePanel, { score: output.score, maxScore: output.maxScore }),
react.createElement("div", { style: { height: "12px" } }),
react.createElement(CheckList, { results: output.results }),
(0, import_react.createElement)(ScorePanel, { score: output.score, maxScore: output.maxScore }),
(0, import_react.createElement)("div", { style: { height: "12px" } }),
(0, import_react.createElement)(CheckList, { results: output.results }),
children ?? null
);
}
exports.CheckList = CheckList;
exports.ContentAnalyzer = ContentAnalyzer;
exports.ScorePanel = ScorePanel;
//# sourceMappingURL=react.cjs.map
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CheckList,
ContentAnalyzer,
ScorePanel
});
//# sourceMappingURL=react.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts","../src/react.ts"],"names":["validateTitle","validateMetaDescription","analyzeKeyphraseOccurrences","calculateKeywordDensity","KEYWORD_DENSITY","stripHtml","getWords","MIN_WORD_COUNT","RECOMMENDED_WORD_COUNT","createElement","useMemo"],"mappings":"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","file":"react.cjs","sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const regex = /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi;\n let match;\n while ((match = regex.exec(html)) !== null) {\n headings.push({\n level: parseInt(match[1]!, 10),\n text: stripHtml(match[2]!),\n });\n }\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — React Components\n// ============================================================================\n\nimport { createElement, useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig } from './types.js';\nimport { analyzeContent } from './analyzer.js';\n\n// --- ScorePanel ---\n\nexport interface ScorePanelProps {\n score: number;\n maxScore: number;\n}\n\nfunction getScoreColor(percentage: number): string {\n if (percentage >= 70) return '#1e8e3e';\n if (percentage >= 40) return '#f29900';\n return '#d93025';\n}\n\nfunction getScoreLabel(percentage: number): string {\n if (percentage >= 70) return 'Good';\n if (percentage >= 40) return 'OK';\n return 'Needs improvement';\n}\n\n/**\n * Displays an overall SEO score as a colored bar with label.\n */\nexport function ScorePanel({ score, maxScore }: ScorePanelProps) {\n const percentage = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0;\n const color = getScoreColor(percentage);\n const label = getScoreLabel(percentage);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n padding: '16px',\n borderRadius: '8px',\n border: '1px solid #e0e0e0',\n backgroundColor: '#fff',\n },\n },\n createElement(\n 'div',\n {\n style: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '8px',\n },\n },\n createElement(\n 'span',\n { style: { fontWeight: 600, fontSize: '14px', color: '#333' } },\n 'SEO Score',\n ),\n createElement(\n 'span',\n { style: { fontWeight: 700, fontSize: '18px', color } },\n `${percentage}%`,\n ),\n ),\n createElement(\n 'div',\n {\n style: {\n width: '100%',\n height: '8px',\n backgroundColor: '#e8e8e8',\n borderRadius: '4px',\n overflow: 'hidden',\n },\n },\n createElement('div', {\n style: {\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: color,\n borderRadius: '4px',\n transition: 'width 0.3s ease',\n },\n }),\n ),\n createElement(\n 'div',\n { style: { marginTop: '4px', fontSize: '12px', color: '#666' } },\n `${label} — ${score}/${maxScore} points`,\n ),\n );\n}\n\n// --- CheckList ---\n\nexport interface CheckListProps {\n results: AnalysisResult[];\n}\n\nconst STATUS_ICONS: Record<string, string> = {\n good: '\\u2705',\n ok: '\\u26a0\\ufe0f',\n poor: '\\u274c',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n good: '#1e8e3e',\n ok: '#f29900',\n poor: '#d93025',\n};\n\n/**\n * Renders analysis results as a list with status icons.\n */\nexport function CheckList({ results }: CheckListProps) {\n return createElement(\n 'ul',\n {\n style: {\n listStyle: 'none',\n padding: 0,\n margin: 0,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n ...results.map((result) =>\n createElement(\n 'li',\n {\n key: result.id,\n style: {\n padding: '10px 12px',\n borderBottom: '1px solid #f0f0f0',\n display: 'flex',\n gap: '10px',\n alignItems: 'flex-start',\n },\n },\n createElement(\n 'span',\n { style: { flexShrink: 0, fontSize: '14px' } },\n STATUS_ICONS[result.status] ?? '',\n ),\n createElement(\n 'div',\n { style: { flex: 1 } },\n createElement(\n 'div',\n {\n style: {\n fontWeight: 600,\n fontSize: '13px',\n color: STATUS_COLORS[result.status] ?? '#333',\n },\n },\n result.title,\n ),\n createElement(\n 'div',\n { style: { fontSize: '12px', color: '#555', marginTop: '2px' } },\n result.description,\n ),\n ),\n ),\n ),\n );\n}\n\n// --- ContentAnalyzer ---\n\nexport interface ContentAnalyzerProps {\n input: ContentAnalysisInput;\n config?: AnalysisConfig;\n children?: ReactNode;\n}\n\n/**\n * All-in-one component that runs analysis and renders score + check list.\n */\nexport function ContentAnalyzer({ input, config, children }: ContentAnalyzerProps) {\n const output = useMemo(() => analyzeContent(input, config), [input, config]);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n createElement(ScorePanel, { score: output.score, maxScore: output.maxScore }),\n createElement('div', { style: { height: '12px' } }),\n createElement(CheckList, { results: output.results }),\n children ?? null,\n );\n}\n"]}
{"version":3,"sources":["../src/react.ts","../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — React Components\n// ============================================================================\n\nimport { createElement, useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig } from './types.js';\nimport { analyzeContent } from './analyzer.js';\n\n// --- ScorePanel ---\n\nexport interface ScorePanelProps {\n score: number;\n maxScore: number;\n}\n\nfunction getScoreColor(percentage: number): string {\n if (percentage >= 70) return '#1e8e3e';\n if (percentage >= 40) return '#f29900';\n return '#d93025';\n}\n\nfunction getScoreLabel(percentage: number): string {\n if (percentage >= 70) return 'Good';\n if (percentage >= 40) return 'OK';\n return 'Needs improvement';\n}\n\n/**\n * Displays an overall SEO score as a colored bar with label.\n */\nexport function ScorePanel({ score, maxScore }: ScorePanelProps) {\n const percentage = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0;\n const color = getScoreColor(percentage);\n const label = getScoreLabel(percentage);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n padding: '16px',\n borderRadius: '8px',\n border: '1px solid #e0e0e0',\n backgroundColor: '#fff',\n },\n },\n createElement(\n 'div',\n {\n style: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '8px',\n },\n },\n createElement(\n 'span',\n { style: { fontWeight: 600, fontSize: '14px', color: '#333' } },\n 'SEO Score',\n ),\n createElement(\n 'span',\n { style: { fontWeight: 700, fontSize: '18px', color } },\n `${percentage}%`,\n ),\n ),\n createElement(\n 'div',\n {\n style: {\n width: '100%',\n height: '8px',\n backgroundColor: '#e8e8e8',\n borderRadius: '4px',\n overflow: 'hidden',\n },\n },\n createElement('div', {\n style: {\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: color,\n borderRadius: '4px',\n transition: 'width 0.3s ease',\n },\n }),\n ),\n createElement(\n 'div',\n { style: { marginTop: '4px', fontSize: '12px', color: '#666' } },\n `${label} — ${score}/${maxScore} points`,\n ),\n );\n}\n\n// --- CheckList ---\n\nexport interface CheckListProps {\n results: AnalysisResult[];\n}\n\nconst STATUS_ICONS: Record<string, string> = {\n good: '\\u2705',\n ok: '\\u26a0\\ufe0f',\n poor: '\\u274c',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n good: '#1e8e3e',\n ok: '#f29900',\n poor: '#d93025',\n};\n\n/**\n * Renders analysis results as a list with status icons.\n */\nexport function CheckList({ results }: CheckListProps) {\n return createElement(\n 'ul',\n {\n style: {\n listStyle: 'none',\n padding: 0,\n margin: 0,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n ...results.map((result) =>\n createElement(\n 'li',\n {\n key: result.id,\n style: {\n padding: '10px 12px',\n borderBottom: '1px solid #f0f0f0',\n display: 'flex',\n gap: '10px',\n alignItems: 'flex-start',\n },\n },\n createElement(\n 'span',\n { style: { flexShrink: 0, fontSize: '14px' } },\n STATUS_ICONS[result.status] ?? '',\n ),\n createElement(\n 'div',\n { style: { flex: 1 } },\n createElement(\n 'div',\n {\n style: {\n fontWeight: 600,\n fontSize: '13px',\n color: STATUS_COLORS[result.status] ?? '#333',\n },\n },\n result.title,\n ),\n createElement(\n 'div',\n { style: { fontSize: '12px', color: '#555', marginTop: '2px' } },\n result.description,\n ),\n ),\n ),\n ),\n );\n}\n\n// --- ContentAnalyzer ---\n\nexport interface ContentAnalyzerProps {\n input: ContentAnalysisInput;\n config?: AnalysisConfig;\n children?: ReactNode;\n}\n\n/**\n * All-in-one component that runs analysis and renders score + check list.\n */\nexport function ContentAnalyzer({ input, config, children }: ContentAnalyzerProps) {\n const output = useMemo(() => analyzeContent(input, config), [input, config]);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n createElement(ScorePanel, { score: output.score, maxScore: output.maxScore }),\n createElement('div', { style: { height: '12px' } }),\n createElement(CheckList, { results: output.results }),\n children ?? null,\n );\n}\n","// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\n/**\n * Parse all headings (h1-h6) from HTML using plain string search.\n * Avoids regex ReDoS on crafted inputs with many repeated heading-like tags.\n */\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const lc = html.toLowerCase();\n let pos = 0;\n\n while (pos < lc.length) {\n // Find the next heading tag of any level\n let earliest = -1;\n let earliestLevel = 0;\n for (let level = 1; level <= 6; level++) {\n const idx = lc.indexOf(`<h${level}`, pos);\n if (idx !== -1 && (earliest === -1 || idx < earliest)) {\n earliest = idx;\n earliestLevel = level;\n }\n }\n if (earliest === -1) break;\n\n const contentStart = lc.indexOf('>', earliest);\n if (contentStart === -1) break;\n\n const closeTag = `</h${earliestLevel}>`;\n const closeIdx = lc.indexOf(closeTag, contentStart + 1);\n if (closeIdx === -1) {\n pos = contentStart + 1;\n continue;\n }\n\n headings.push({\n level: earliestLevel,\n text: stripHtml(html.slice(contentStart + 1, closeIdx)),\n });\n pos = closeIdx + closeTag.length;\n }\n\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core"]}

@@ -1,5 +0,6 @@

import { createElement, useMemo } from 'react';
import { validateTitle, validateMetaDescription, analyzeKeyphraseOccurrences, calculateKeywordDensity, KEYWORD_DENSITY, getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT, stripHtml } from '@power-seo/core';
// src/react.ts
import { createElement, useMemo } from "react";
// src/react.ts
// src/checks/title.ts
import { validateTitle } from "@power-seo/core";
function checkTitle(input) {

@@ -73,2 +74,5 @@ const results = [];

}
// src/checks/meta-description.ts
import { validateMetaDescription } from "@power-seo/core";
function checkMetaDescription(input) {

@@ -142,2 +146,9 @@ const results = [];

}
// src/checks/keyphrase-usage.ts
import {
analyzeKeyphraseOccurrences,
calculateKeywordDensity,
KEYWORD_DENSITY
} from "@power-seo/core";
function checkKeyphraseUsage(input) {

@@ -231,11 +242,33 @@ const results = [];

}
// src/checks/headings.ts
import { stripHtml } from "@power-seo/core";
function parseHeadings(html) {
const headings = [];
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
const lc = html.toLowerCase();
let pos = 0;
while (pos < lc.length) {
let earliest = -1;
let earliestLevel = 0;
for (let level = 1; level <= 6; level++) {
const idx = lc.indexOf(`<h${level}`, pos);
if (idx !== -1 && (earliest === -1 || idx < earliest)) {
earliest = idx;
earliestLevel = level;
}
}
if (earliest === -1) break;
const contentStart = lc.indexOf(">", earliest);
if (contentStart === -1) break;
const closeTag = `</h${earliestLevel}>`;
const closeIdx = lc.indexOf(closeTag, contentStart + 1);
if (closeIdx === -1) {
pos = contentStart + 1;
continue;
}
headings.push({
level: parseInt(match[1], 10),
text: stripHtml(match[2])
level: earliestLevel,
text: stripHtml(html.slice(contentStart + 1, closeIdx))
});
pos = closeIdx + closeTag.length;
}

@@ -332,2 +365,5 @@ return headings;

}
// src/checks/word-count.ts
import { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from "@power-seo/core";
function checkWordCount(input) {

@@ -672,5 +708,7 @@ const words = getWords(input.content);

}
export { CheckList, ContentAnalyzer, ScorePanel };
//# sourceMappingURL=react.js.map
export {
CheckList,
ContentAnalyzer,
ScorePanel
};
//# sourceMappingURL=react.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts","../src/react.ts"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const regex = /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi;\n let match;\n while ((match = regex.exec(html)) !== null) {\n headings.push({\n level: parseInt(match[1]!, 10),\n text: stripHtml(match[2]!),\n });\n }\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — React Components\n// ============================================================================\n\nimport { createElement, useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig } from './types.js';\nimport { analyzeContent } from './analyzer.js';\n\n// --- ScorePanel ---\n\nexport interface ScorePanelProps {\n score: number;\n maxScore: number;\n}\n\nfunction getScoreColor(percentage: number): string {\n if (percentage >= 70) return '#1e8e3e';\n if (percentage >= 40) return '#f29900';\n return '#d93025';\n}\n\nfunction getScoreLabel(percentage: number): string {\n if (percentage >= 70) return 'Good';\n if (percentage >= 40) return 'OK';\n return 'Needs improvement';\n}\n\n/**\n * Displays an overall SEO score as a colored bar with label.\n */\nexport function ScorePanel({ score, maxScore }: ScorePanelProps) {\n const percentage = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0;\n const color = getScoreColor(percentage);\n const label = getScoreLabel(percentage);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n padding: '16px',\n borderRadius: '8px',\n border: '1px solid #e0e0e0',\n backgroundColor: '#fff',\n },\n },\n createElement(\n 'div',\n {\n style: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '8px',\n },\n },\n createElement(\n 'span',\n { style: { fontWeight: 600, fontSize: '14px', color: '#333' } },\n 'SEO Score',\n ),\n createElement(\n 'span',\n { style: { fontWeight: 700, fontSize: '18px', color } },\n `${percentage}%`,\n ),\n ),\n createElement(\n 'div',\n {\n style: {\n width: '100%',\n height: '8px',\n backgroundColor: '#e8e8e8',\n borderRadius: '4px',\n overflow: 'hidden',\n },\n },\n createElement('div', {\n style: {\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: color,\n borderRadius: '4px',\n transition: 'width 0.3s ease',\n },\n }),\n ),\n createElement(\n 'div',\n { style: { marginTop: '4px', fontSize: '12px', color: '#666' } },\n `${label} — ${score}/${maxScore} points`,\n ),\n );\n}\n\n// --- CheckList ---\n\nexport interface CheckListProps {\n results: AnalysisResult[];\n}\n\nconst STATUS_ICONS: Record<string, string> = {\n good: '\\u2705',\n ok: '\\u26a0\\ufe0f',\n poor: '\\u274c',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n good: '#1e8e3e',\n ok: '#f29900',\n poor: '#d93025',\n};\n\n/**\n * Renders analysis results as a list with status icons.\n */\nexport function CheckList({ results }: CheckListProps) {\n return createElement(\n 'ul',\n {\n style: {\n listStyle: 'none',\n padding: 0,\n margin: 0,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n ...results.map((result) =>\n createElement(\n 'li',\n {\n key: result.id,\n style: {\n padding: '10px 12px',\n borderBottom: '1px solid #f0f0f0',\n display: 'flex',\n gap: '10px',\n alignItems: 'flex-start',\n },\n },\n createElement(\n 'span',\n { style: { flexShrink: 0, fontSize: '14px' } },\n STATUS_ICONS[result.status] ?? '',\n ),\n createElement(\n 'div',\n { style: { flex: 1 } },\n createElement(\n 'div',\n {\n style: {\n fontWeight: 600,\n fontSize: '13px',\n color: STATUS_COLORS[result.status] ?? '#333',\n },\n },\n result.title,\n ),\n createElement(\n 'div',\n { style: { fontSize: '12px', color: '#555', marginTop: '2px' } },\n result.description,\n ),\n ),\n ),\n ),\n );\n}\n\n// --- ContentAnalyzer ---\n\nexport interface ContentAnalyzerProps {\n input: ContentAnalysisInput;\n config?: AnalysisConfig;\n children?: ReactNode;\n}\n\n/**\n * All-in-one component that runs analysis and renders score + check list.\n */\nexport function ContentAnalyzer({ input, config, children }: ContentAnalyzerProps) {\n const output = useMemo(() => analyzeContent(input, config), [input, config]);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n createElement(ScorePanel, { score: output.score, maxScore: output.maxScore }),\n createElement('div', { style: { height: '12px' } }),\n createElement(CheckList, { results: output.results }),\n children ?? null,\n );\n}\n"]}
{"version":3,"sources":["../src/react.ts","../src/checks/title.ts","../src/checks/meta-description.ts","../src/checks/keyphrase-usage.ts","../src/checks/headings.ts","../src/checks/word-count.ts","../src/checks/images.ts","../src/checks/links.ts","../src/analyzer.ts"],"sourcesContent":["// ============================================================================\n// @power-seo/content-analysis — React Components\n// ============================================================================\n\nimport { createElement, useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig } from './types.js';\nimport { analyzeContent } from './analyzer.js';\n\n// --- ScorePanel ---\n\nexport interface ScorePanelProps {\n score: number;\n maxScore: number;\n}\n\nfunction getScoreColor(percentage: number): string {\n if (percentage >= 70) return '#1e8e3e';\n if (percentage >= 40) return '#f29900';\n return '#d93025';\n}\n\nfunction getScoreLabel(percentage: number): string {\n if (percentage >= 70) return 'Good';\n if (percentage >= 40) return 'OK';\n return 'Needs improvement';\n}\n\n/**\n * Displays an overall SEO score as a colored bar with label.\n */\nexport function ScorePanel({ score, maxScore }: ScorePanelProps) {\n const percentage = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0;\n const color = getScoreColor(percentage);\n const label = getScoreLabel(percentage);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n padding: '16px',\n borderRadius: '8px',\n border: '1px solid #e0e0e0',\n backgroundColor: '#fff',\n },\n },\n createElement(\n 'div',\n {\n style: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: '8px',\n },\n },\n createElement(\n 'span',\n { style: { fontWeight: 600, fontSize: '14px', color: '#333' } },\n 'SEO Score',\n ),\n createElement(\n 'span',\n { style: { fontWeight: 700, fontSize: '18px', color } },\n `${percentage}%`,\n ),\n ),\n createElement(\n 'div',\n {\n style: {\n width: '100%',\n height: '8px',\n backgroundColor: '#e8e8e8',\n borderRadius: '4px',\n overflow: 'hidden',\n },\n },\n createElement('div', {\n style: {\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: color,\n borderRadius: '4px',\n transition: 'width 0.3s ease',\n },\n }),\n ),\n createElement(\n 'div',\n { style: { marginTop: '4px', fontSize: '12px', color: '#666' } },\n `${label} — ${score}/${maxScore} points`,\n ),\n );\n}\n\n// --- CheckList ---\n\nexport interface CheckListProps {\n results: AnalysisResult[];\n}\n\nconst STATUS_ICONS: Record<string, string> = {\n good: '\\u2705',\n ok: '\\u26a0\\ufe0f',\n poor: '\\u274c',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n good: '#1e8e3e',\n ok: '#f29900',\n poor: '#d93025',\n};\n\n/**\n * Renders analysis results as a list with status icons.\n */\nexport function CheckList({ results }: CheckListProps) {\n return createElement(\n 'ul',\n {\n style: {\n listStyle: 'none',\n padding: 0,\n margin: 0,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n ...results.map((result) =>\n createElement(\n 'li',\n {\n key: result.id,\n style: {\n padding: '10px 12px',\n borderBottom: '1px solid #f0f0f0',\n display: 'flex',\n gap: '10px',\n alignItems: 'flex-start',\n },\n },\n createElement(\n 'span',\n { style: { flexShrink: 0, fontSize: '14px' } },\n STATUS_ICONS[result.status] ?? '',\n ),\n createElement(\n 'div',\n { style: { flex: 1 } },\n createElement(\n 'div',\n {\n style: {\n fontWeight: 600,\n fontSize: '13px',\n color: STATUS_COLORS[result.status] ?? '#333',\n },\n },\n result.title,\n ),\n createElement(\n 'div',\n { style: { fontSize: '12px', color: '#555', marginTop: '2px' } },\n result.description,\n ),\n ),\n ),\n ),\n );\n}\n\n// --- ContentAnalyzer ---\n\nexport interface ContentAnalyzerProps {\n input: ContentAnalysisInput;\n config?: AnalysisConfig;\n children?: ReactNode;\n}\n\n/**\n * All-in-one component that runs analysis and renders score + check list.\n */\nexport function ContentAnalyzer({ input, config, children }: ContentAnalyzerProps) {\n const output = useMemo(() => analyzeContent(input, config), [input, config]);\n\n return createElement(\n 'div',\n {\n style: {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n },\n createElement(ScorePanel, { score: output.score, maxScore: output.maxScore }),\n createElement('div', { style: { height: '12px' } }),\n createElement(CheckList, { results: output.results }),\n children ?? null,\n );\n}\n","// ============================================================================\n// @power-seo/content-analysis — Title Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateTitle } from '@power-seo/core';\n\nexport function checkTitle(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { title, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!title || title.trim().length === 0) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: 'No title has been set. Add a title to improve search visibility.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateTitle(title);\n\n if (!validation.valid) {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-presence',\n title: 'SEO title',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in title check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const titleLower = title.toLowerCase();\n\n if (titleLower.includes(kp)) {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description: 'The focus keyphrase appears in the SEO title. Good job!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'title-keyphrase',\n title: 'Keyphrase in title',\n description:\n 'The focus keyphrase does not appear in the SEO title. Add it to improve relevance.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Meta Description Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { validateMetaDescription } from '@power-seo/core';\n\nexport function checkMetaDescription(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { metaDescription, focusKeyphrase } = input;\n\n // --- Presence & validity check ---\n if (!metaDescription || metaDescription.trim().length === 0) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description:\n 'No meta description has been set. Add one to control how your page appears in search results.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n return results;\n }\n\n const validation = validateMetaDescription(metaDescription);\n\n if (!validation.valid) {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else if (validation.severity === 'warning') {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-presence',\n title: 'Meta description',\n description: validation.message,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in meta description check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const descLower = metaDescription.toLowerCase();\n\n if (descLower.includes(kp)) {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description: 'The focus keyphrase appears in the meta description. Well done!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'meta-description-keyphrase',\n title: 'Keyphrase in meta description',\n description:\n 'The focus keyphrase does not appear in the meta description. Add it to improve click-through rate.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Keyphrase Usage Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport {\n analyzeKeyphraseOccurrences,\n calculateKeywordDensity,\n KEYWORD_DENSITY,\n} from '@power-seo/core';\n\nexport function checkKeyphraseUsage(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { focusKeyphrase, title, metaDescription, content, slug, images } = input;\n\n if (!focusKeyphrase || focusKeyphrase.trim().length === 0) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: 'No focus keyphrase set. Set one to get keyphrase analysis.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n return results;\n }\n\n const occurrences = analyzeKeyphraseOccurrences({\n keyphrase: focusKeyphrase,\n title,\n metaDescription,\n content,\n slug,\n images,\n });\n\n const densityResult = calculateKeywordDensity(focusKeyphrase, content);\n\n // --- Density check ---\n if (densityResult.density < KEYWORD_DENSITY.MIN) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which is below the recommended minimum of ${KEYWORD_DENSITY.MIN}%. Use the keyphrase more often.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (densityResult.density > KEYWORD_DENSITY.MAX) {\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%, which exceeds the recommended maximum of ${KEYWORD_DENSITY.MAX}%. Reduce usage to avoid keyword stuffing.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n } else if (\n densityResult.density >= KEYWORD_DENSITY.MIN &&\n densityResult.density <= KEYWORD_DENSITY.MAX\n ) {\n const isOptimal = Math.abs(densityResult.density - KEYWORD_DENSITY.OPTIMAL) < 0.5;\n results.push({\n id: 'keyphrase-density',\n title: 'Keyphrase density',\n description: `Keyphrase density is ${densityResult.density}%.${isOptimal ? ' Great — this is close to the optimal density.' : ' This is within the recommended range.'}`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n // --- Distribution check ---\n const distributionPoints: string[] = [];\n if (!occurrences.inFirstParagraph) distributionPoints.push('introduction');\n if (!occurrences.inH1 && occurrences.inHeadings === 0) distributionPoints.push('headings');\n if (!occurrences.inSlug) distributionPoints.push('slug');\n if (occurrences.inAltText === 0 && images && images.length > 0)\n distributionPoints.push('image alt text');\n\n if (distributionPoints.length === 0) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description:\n 'The focus keyphrase is well-distributed across the introduction, headings, slug, and image alt text.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (distributionPoints.length <= 2) {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `Consider adding the keyphrase to: ${distributionPoints.join(', ')}.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'keyphrase-distribution',\n title: 'Keyphrase distribution',\n description: `The keyphrase is missing from: ${distributionPoints.join(', ')}. Distribute it more broadly.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Headings Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { stripHtml } from '@power-seo/core';\n\ninterface HeadingInfo {\n level: number;\n text: string;\n}\n\n/**\n * Parse all headings (h1-h6) from HTML using plain string search.\n * Avoids regex ReDoS on crafted inputs with many repeated heading-like tags.\n */\nfunction parseHeadings(html: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n const lc = html.toLowerCase();\n let pos = 0;\n\n while (pos < lc.length) {\n // Find the next heading tag of any level\n let earliest = -1;\n let earliestLevel = 0;\n for (let level = 1; level <= 6; level++) {\n const idx = lc.indexOf(`<h${level}`, pos);\n if (idx !== -1 && (earliest === -1 || idx < earliest)) {\n earliest = idx;\n earliestLevel = level;\n }\n }\n if (earliest === -1) break;\n\n const contentStart = lc.indexOf('>', earliest);\n if (contentStart === -1) break;\n\n const closeTag = `</h${earliestLevel}>`;\n const closeIdx = lc.indexOf(closeTag, contentStart + 1);\n if (closeIdx === -1) {\n pos = contentStart + 1;\n continue;\n }\n\n headings.push({\n level: earliestLevel,\n text: stripHtml(html.slice(contentStart + 1, closeIdx)),\n });\n pos = closeIdx + closeTag.length;\n }\n\n return headings;\n}\n\nexport function checkHeadings(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { content, focusKeyphrase } = input;\n const headings = parseHeadings(content);\n\n // --- H1 & structure check ---\n const h1s = headings.filter((h) => h.level === 1);\n\n if (h1s.length === 0) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'No H1 heading found. Add exactly one H1 as the main heading of your page.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else if (h1s.length > 1) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: `Found ${h1s.length} H1 headings. Use exactly one H1 per page for proper SEO.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n // Check heading hierarchy\n let hasSkippedLevel = false;\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!;\n const curr = headings[i]!;\n if (curr.level > prev.level + 1) {\n hasSkippedLevel = true;\n break;\n }\n }\n\n if (hasSkippedLevel) {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description:\n 'The heading hierarchy skips levels (e.g., H2 to H4). Use sequential heading levels for better accessibility and SEO.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-structure',\n title: 'Heading structure',\n description: 'The heading structure looks good with a single H1 and proper hierarchy.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n }\n\n // --- Keyphrase in headings check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const subheadings = headings.filter((h) => h.level >= 2);\n const hasKeyphraseInSubheading = subheadings.some((h) => h.text.toLowerCase().includes(kp));\n\n if (subheadings.length === 0) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'No subheadings (H2-H6) found. Add subheadings to structure your content and include the focus keyphrase.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else if (hasKeyphraseInSubheading) {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description: 'The focus keyphrase appears in at least one subheading. Nice!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'heading-keyphrase',\n title: 'Keyphrase in subheadings',\n description:\n 'The focus keyphrase does not appear in any subheading. Consider adding it to an H2 or H3.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Word Count Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\nimport { getWords, MIN_WORD_COUNT, RECOMMENDED_WORD_COUNT } from '@power-seo/core';\n\nexport function checkWordCount(input: ContentAnalysisInput): AnalysisResult {\n const words = getWords(input.content);\n const count = words.length;\n\n if (count < MIN_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words, which is below the recommended minimum of ${MIN_WORD_COUNT}. Add more content to improve SEO.`,\n status: 'poor',\n score: 1,\n maxScore: 5,\n };\n }\n\n if (count < RECOMMENDED_WORD_COUNT) {\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Consider expanding to at least ${RECOMMENDED_WORD_COUNT} words for more comprehensive coverage.`,\n status: 'ok',\n score: 3,\n maxScore: 5,\n };\n }\n\n return {\n id: 'word-count',\n title: 'Word count',\n description: `The content is ${count} words. Good — this provides enough depth for search engines.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n };\n}\n","// ============================================================================\n// @power-seo/content-analysis — Images Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkImages(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { images, focusKeyphrase } = input;\n\n if (!images || images.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'No images found. Consider adding images to make your content more engaging.',\n status: 'ok',\n score: 3,\n maxScore: 5,\n });\n return results;\n }\n\n // --- Alt text check ---\n const missingAlt = images.filter((img) => !img.alt || img.alt.trim().length === 0);\n\n if (missingAlt.length === 0) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: 'All images have alt text. Great for accessibility and SEO!',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else if (missingAlt.length === images.length) {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description:\n 'None of the images have alt text. Add descriptive alt attributes for accessibility and SEO.',\n status: 'poor',\n score: 0,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-alt',\n title: 'Image alt attributes',\n description: `${missingAlt.length} of ${images.length} images are missing alt text. Add alt attributes to all images.`,\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n\n // --- Keyphrase in alt text check ---\n if (focusKeyphrase && focusKeyphrase.trim().length > 0) {\n const kp = focusKeyphrase.toLowerCase().trim();\n const hasKeyphraseInAlt = images.some((img) => img.alt && img.alt.toLowerCase().includes(kp));\n\n if (hasKeyphraseInAlt) {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description: 'The focus keyphrase appears in at least one image alt attribute.',\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'image-keyphrase',\n title: 'Keyphrase in image alt',\n description:\n 'The focus keyphrase does not appear in any image alt attribute. Add it to at least one image.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n }\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Links Check\n// ============================================================================\n\nimport type { ContentAnalysisInput, AnalysisResult } from '@power-seo/core';\n\nexport function checkLinks(input: ContentAnalysisInput): AnalysisResult[] {\n const results: AnalysisResult[] = [];\n const { internalLinks, externalLinks } = input;\n\n const hasInternal = internalLinks && internalLinks.length > 0;\n const hasExternal = externalLinks && externalLinks.length > 0;\n\n if (!hasInternal) {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description:\n 'No internal links found. Add links to other pages on your site to improve crawlability and distribute link equity.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'internal-links',\n title: 'Internal links',\n description: `Found ${internalLinks!.length} internal link${internalLinks!.length === 1 ? '' : 's'}. Good for site structure and SEO.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n if (!hasExternal) {\n results.push({\n id: 'external-links',\n title: 'External links',\n description:\n 'No external links found. Consider adding outbound links to authoritative sources to strengthen your content.',\n status: 'ok',\n score: 2,\n maxScore: 5,\n });\n } else {\n results.push({\n id: 'external-links',\n title: 'External links',\n description: `Found ${externalLinks!.length} external link${externalLinks!.length === 1 ? '' : 's'}. Linking to quality sources adds credibility.`,\n status: 'good',\n score: 5,\n maxScore: 5,\n });\n }\n\n return results;\n}\n","// ============================================================================\n// @power-seo/content-analysis — Content Analyzer Orchestrator\n// ============================================================================\n\nimport type { ContentAnalysisInput, ContentAnalysisOutput, AnalysisResult } from '@power-seo/core';\nimport type { AnalysisConfig, CheckId } from './types.js';\nimport { checkTitle } from './checks/title.js';\nimport { checkMetaDescription } from './checks/meta-description.js';\nimport { checkKeyphraseUsage } from './checks/keyphrase-usage.js';\nimport { checkHeadings } from './checks/headings.js';\nimport { checkWordCount } from './checks/word-count.js';\nimport { checkImages } from './checks/images.js';\nimport { checkLinks } from './checks/links.js';\n\n/**\n * Run all SEO content analysis checks and return aggregated results.\n *\n * @example\n * ```ts\n * const output = analyzeContent({\n * title: 'My Blog Post',\n * metaDescription: 'A description of my blog post about SEO.',\n * content: '<h1>My Blog Post</h1><p>Content goes here...</p>',\n * focusKeyphrase: 'blog post',\n * });\n * console.log(output.score, output.maxScore, output.recommendations);\n * ```\n */\nexport function analyzeContent(\n input: ContentAnalysisInput,\n config?: AnalysisConfig,\n): ContentAnalysisOutput {\n const disabled = new Set<CheckId>(config?.disabledChecks ?? []);\n\n const allResults: AnalysisResult[] = [];\n\n // Run each check group and collect results\n const titleResults = checkTitle(input);\n const metaResults = checkMetaDescription(input);\n const keyphraseResults = checkKeyphraseUsage(input);\n const headingResults = checkHeadings(input);\n const wordCountResult = checkWordCount(input);\n const imageResults = checkImages(input);\n const linkResults = checkLinks(input);\n\n // Flatten all results\n const candidateResults = [\n ...titleResults,\n ...metaResults,\n ...keyphraseResults,\n ...headingResults,\n wordCountResult,\n ...imageResults,\n ...linkResults,\n ];\n\n // Filter out disabled checks\n for (const result of candidateResults) {\n if (!disabled.has(result.id as CheckId)) {\n allResults.push(result);\n }\n }\n\n // Sum scores\n const score = allResults.reduce((sum, r) => sum + r.score, 0);\n const maxScore = allResults.reduce((sum, r) => sum + r.maxScore, 0);\n\n // Generate recommendations from poor/ok results\n const recommendations = allResults\n .filter((r) => r.status === 'poor' || r.status === 'ok')\n .map((r) => r.description);\n\n return {\n score,\n maxScore,\n results: allResults,\n recommendations,\n };\n}\n"],"mappings":"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","names":[]}
{
"name": "@power-seo/content-analysis",
"version": "1.0.6",
"version": "1.0.7",
"description": "Yoast-style SEO content analysis engine with scoring, checks, and React components",

@@ -48,7 +48,7 @@ "license": "MIT",

"@testing-library/react": "^16.0.0",
"@types/react": "^19.0.0",
"@types/react": "^19.2.14",
"jsdom": "^25.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rimraf": "^6.0.0",
"rimraf": "^6.1.3",
"tsup": "^8.3.0",

@@ -55,0 +55,0 @@ "typescript": "^5.7.0",

+100
-93

@@ -15,5 +15,5 @@ # @power-seo/content-analysis

`@power-seo/content-analysis` delivers a comprehensive, WordPress SEO plugin–style scoring pipeline for evaluating text content, comparable to Yoast SEO, All in One SEO (AIOSEO), Rank Math, SEOPress, and The SEO Framework. Provide a page title, meta description, body content, focus keyphrase, images, and links — get back structured `good` / `needs-improvement` / `poor` results across all critical SEO factors. Run it server-side in a CMS, client-side in a React editor, or inside a CI content quality gate. All 13 analysis checks are fully configurable and tree-shakeable.
`@power-seo/content-analysis` delivers a comprehensive, WordPress SEO plugin–style scoring pipeline for evaluating text content, comparable to Yoast SEO, All in One SEO (AIOSEO), Rank Math, SEOPress, and The SEO Framework. Provide a page title, meta description, body content, focus keyphrase, images, and links — get back structured `good` / `ok` / `poor` results across all critical SEO factors. Run it server-side in a CMS, client-side in a React editor, or inside a CI content quality gate. All 13 analysis checks are fully configurable and tree-shakeable.
> **Zero runtime dependencies** — only `@power-seo/core` as a peer.
> **Zero external runtime dependencies** — `@power-seo/core` is a direct dependency bundled with this package; no extra install needed.

@@ -24,12 +24,12 @@ ---

| | Without | With |
|---|---|---|
| Keyphrase check | ❌ Manual grep | ✅ Density + distribution scoring |
| Title validation | ❌ Eye-check only | ✅ Presence, length, keyphrase match |
| Meta description | ❌ Unchecked | ✅ Length (120–160 chars) + keyphrase |
| Heading structure | ❌ Missed H1s | ✅ H1 hierarchy + keyphrase in subheadings |
| Image alt text | ❌ Skipped | ✅ Alt presence + keyphrase in alt |
| Link analysis | ❌ Unknown | ✅ Internal + external link presence |
| SEO score | ❌ Guesswork | ✅ Aggregate score with per-check breakdown |
| Framework support | ❌ WordPress-only | ✅ Next.js, Remix, Vite, Node.js, Edge |
| | Without | With |
| ----------------- | ----------------- | ------------------------------------------- |
| Keyphrase check | ❌ Manual grep | ✅ Density + distribution scoring |
| Title validation | ❌ Eye-check only | ✅ Presence, length, keyphrase match |
| Meta description | ❌ Unchecked | ✅ Length (120–160 chars) + keyphrase |
| Heading structure | ❌ Missed H1s | ✅ H1 hierarchy + keyphrase in subheadings |
| Image alt text | ❌ Skipped | ✅ Alt presence + keyphrase in alt |
| Link analysis | ❌ Unknown | ✅ Internal + external link presence |
| SEO score | ❌ Guesswork | ✅ Aggregate score with per-check breakdown |
| Framework support | ❌ WordPress-only | ✅ Next.js, Remix, Vite, Node.js, Edge |

@@ -65,16 +65,16 @@ ![SEO Score Dashboard](../../image/content-analysis/score-dashboard.svg)

| ------------------------------ | :-------------------------: | :-------: | :------: | :----------: | :----------: |
| Keyphrase density check | ✅ | ✅ | ❌ | Partial | ❌ |
| Keyphrase distribution | ✅ | ✅ | ❌ | ❌ | ❌ |
| Title + meta validation | ✅ | ✅ | ❌ | Partial | ❌ |
| Heading structure check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Image alt + keyphrase check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Internal / external link check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Aggregate SEO score | ✅ | ✅ | ❌ | Partial | ❌ |
| Per-check disable config | ✅ | ❌ | ❌ | ❌ | ❌ |
| Works outside WordPress | ✅ | ❌ | ✅ | ✅ | ✅ |
| TypeScript-first | ✅ | ❌ | Partial | ❌ | ❌ |
| Tree-shakeable | ✅ | ❌ | Partial | ❌ | ❌ |
| React UI components | ✅ | ✅ | ❌ | ❌ | ❌ |
| CI / Node.js usage | ✅ | ❌ | ❌ | ✅ | ❌ |
| Zero runtime dependencies | ✅ | ❌ | ❌ | ❌ | ❌ |
| Keyphrase density check | ✅ | ✅ | ❌ | Partial | ❌ |
| Keyphrase distribution | ✅ | ✅ | ❌ | ❌ | ❌ |
| Title + meta validation | ✅ | ✅ | ❌ | Partial | ❌ |
| Heading structure check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Image alt + keyphrase check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Internal / external link check | ✅ | ✅ | ❌ | ❌ | ❌ |
| Aggregate SEO score | ✅ | ✅ | ❌ | Partial | ❌ |
| Per-check disable config | ✅ | ❌ | ❌ | ❌ | ❌ |
| Works outside WordPress | ✅ | ❌ | ✅ | ✅ | ✅ |
| TypeScript-first | ✅ | ❌ | Partial | ❌ | ❌ |
| Tree-shakeable | ✅ | ❌ | Partial | ❌ | ❌ |
| React UI components | ✅ | ✅ | ❌ | ❌ | ❌ |
| CI / Node.js usage | ✅ | ❌ | ❌ | ✅ | ❌ |
| Zero runtime dependencies | ✅ | ❌ | ❌ | ❌ | ❌ |

@@ -88,11 +88,11 @@ ![Keyword Density Analysis](../../image/content-analysis/keyword-density.svg)

```bash
npm install @power-seo/content-analysis @power-seo/core
npm install @power-seo/content-analysis
```
```bash
yarn add @power-seo/content-analysis @power-seo/core
yarn add @power-seo/content-analysis
```
```bash
pnpm add @power-seo/content-analysis @power-seo/core
pnpm add @power-seo/content-analysis
```

@@ -110,10 +110,10 @@

metaDescription: 'Discover the best running shoes for beginners with our expert guide.',
keyphrase: 'running shoes for beginners',
focusKeyphrase: 'running shoes for beginners',
content: '<h1>Best Running Shoes</h1><p>Finding the right running shoes...</p>',
url: 'https://example.com/best-running-shoes',
});
console.log(result.overallStatus); // "good" | "needs-improvement" | "poor"
console.log(result.score); // e.g. 38
console.log(result.maxScore); // e.g. 55
console.log(result.results);
// [{ id: 'title-presence', status: 'good', message: '...' }, ...]
// [{ id: 'title-presence', status: 'good', description: '...', score: 5, maxScore: 5 }, ...]
```

@@ -124,4 +124,5 @@

**Status thresholds (per check):**
- `good` — check fully passes
- `needs-improvement` — check partially passes
- `ok` — check partially passes
- `poor` — check fails

@@ -142,6 +143,6 @@

title: 'Next.js SEO Best Practices',
metaDescription: 'Learn how to optimize your Next.js app for search engines with meta tags and structured data.',
keyphrase: 'next.js seo',
metaDescription:
'Learn how to optimize your Next.js app for search engines with meta tags and structured data.',
focusKeyphrase: 'next.js seo',
content: htmlString,
url: 'https://example.com/nextjs-seo',
images: imageList,

@@ -152,4 +153,5 @@ internalLinks: internalLinks,

// output.overallStatus → 'good' | 'needs-improvement' | 'poor'
// output.results → AnalysisResult[]
// output.score → number (sum of all check scores)
// output.maxScore → number (maximum possible score)
// output.results → AnalysisResult[]
```

@@ -173,11 +175,13 @@

const titleResults = checkTitle({
content: '',
title: 'React SEO Guide',
keyphrase: 'react seo',
content: '',
focusKeyphrase: 'react seo',
});
// [{ id: 'title-presence', status: 'good', message: '...' },
// { id: 'title-keyphrase', status: 'good', message: '...' }]
// [
// { id: 'title-presence', title: 'SEO title', description: '...', status: 'good', score: 5, maxScore: 5 },
// { id: 'title-keyphrase', title: 'Keyphrase in title', description: '...', status: 'good', score: 5, maxScore: 5 }
// ]
const wcResult = checkWordCount({ content: shortHtml });
// { id: 'word-count', status: 'poor', message: 'Content is 180 words, below minimum of 300.' }
// { id: 'word-count', title: 'Word count', description: 'The content is 180 words...', status: 'poor', score: 1, maxScore: 5 }
```

@@ -187,3 +191,3 @@

Pass `config.disabledChecks` to skip checks that don't apply to your content type:
Pass `config.disabledChecks` to exclude specific checks from the output. Checks are still executed internally but their results are filtered from `output.results` and excluded from `score`/`maxScore` totals. Invalid check IDs are silently ignored.

@@ -204,2 +208,3 @@ ```ts

import { ContentAnalyzer, ScorePanel, CheckList } from '@power-seo/content-analysis/react';
import { analyzeContent } from '@power-seo/content-analysis';
import type { ContentAnalysisInput } from '@power-seo/content-analysis';

@@ -231,3 +236,3 @@

const output = analyzeContent({ title, metaDescription, keyphrase, content });
const output = analyzeContent({ title, metaDescription, focusKeyphrase, content });

@@ -238,3 +243,3 @@ const failures = output.results.filter((r) => r.status === 'poor');

console.error('SEO checks failed:');
failures.forEach((r) => console.error(' ✗', r.message));
failures.forEach((r) => console.error(' ✗', r.description));
process.exit(1);

@@ -250,6 +255,6 @@ }

| Import | Description |
| --- | --- |
| `@power-seo/content-analysis` | Core analyzer and individual check functions |
| `@power-seo/content-analysis/react` | React components for analysis UI |
| Import | Description |
| ----------------------------------- | -------------------------------------------- |
| `@power-seo/content-analysis` | Core analyzer and individual check functions |
| `@power-seo/content-analysis/react` | React components for analysis UI |

@@ -267,32 +272,32 @@ ### `analyzeContent()`

| Prop | Type | Required | Description |
| ----------------- | -------------------------------------- | -------- | ---------------------------------------------- |
| `content` | `string` | ✅ | Body HTML string |
| `title` | `string` | — | Page `<title>` content |
| `metaDescription` | `string` | — | Meta description content |
| `keyphrase` | `string` | — | Focus keyphrase to analyze against |
| `url` | `string` | — | Page URL (used for slug analysis) |
| `images` | `Array<{ src: string; alt?: string }>` | — | Images found on the page |
| `internalLinks` | `string[]` | — | Internal link URLs |
| `externalLinks` | `string[]` | — | External link URLs |
| Prop | Type | Required | Description |
| ----------------- | -------------------------------------- | -------- | ------------------------------------------- |
| `content` | `string` | ✅ | Body HTML string |
| `title` | `string` | — | Page `<title>` content |
| `metaDescription` | `string` | — | Meta description content |
| `focusKeyphrase` | `string` | — | Focus keyphrase to analyze against |
| `slug` | `string` | — | URL slug (used for keyphrase-in-slug check) |
| `images` | `Array<{ src: string; alt?: string }>` | — | Images found on the page |
| `internalLinks` | `string[]` | — | Internal link URLs |
| `externalLinks` | `string[]` | — | External link URLs |
#### `ContentAnalysisOutput`
| Field | Type | Description |
| --------------- | ------------------ | --------------------------------------------------------- |
| `overallStatus` | `AnalysisStatus` | `'good'` \| `'needs-improvement'` \| `'poor'` |
| `score` | `number` | Sum of all individual check scores |
| `maxScore` | `number` | Maximum possible score (varies by enabled checks) |
| `results` | `AnalysisResult[]` | Per-check results |
| `recommendations` | `string[]` | Descriptions from all failed or partial checks |
| Field | Type | Description |
| ----------------- | ------------------ | ------------------------------------------------- |
| `score` | `number` | Sum of all individual check scores |
| `maxScore` | `number` | Maximum possible score (varies by enabled checks) |
| `results` | `AnalysisResult[]` | Per-check results |
| `recommendations` | `string[]` | Descriptions from all failed or partial checks |
#### `AnalysisResult`
| Field | Type | Description |
| --------- | ---------------- | --------------------------------------------- |
| `id` | `CheckId` | Unique check identifier (see table below) |
| `status` | `AnalysisStatus` | `'good'` \| `'needs-improvement'` \| `'poor'` |
| `message` | `string` | Human-readable actionable feedback |
| `score` | `number` | Points earned for this check |
| `maxScore`| `number` | Maximum points for this check |
| Field | Type | Description |
| ------------- | ---------------- | ------------------------------------------------------ |
| `id` | `string` | Unique check identifier (one of the `CheckId` values) |
| `title` | `string` | Short display label for the check (e.g. `"SEO title"`) |
| `description` | `string` | Human-readable actionable feedback |
| `status` | `AnalysisStatus` | `'good'` \| `'ok'` \| `'poor'` |
| `score` | `number` | Points earned for this check |
| `maxScore` | `number` | Maximum points for this check |

@@ -307,22 +312,24 @@ #### `AnalysisConfig`

| Function | Check ID(s) | Checks For |
| ----------------------------- | -------------------------------------------------------------- | -------------------------------------------------- |
| `checkTitle(input)` | `title-presence`, `title-keyphrase` | Title presence, length (30–60 chars), keyphrase |
| `checkMetaDescription(input)` | `meta-description-presence`, `meta-description-keyphrase` | Description presence, length (120–160 chars), keyphrase |
| `checkKeyphraseUsage(input)` | `keyphrase-density`, `keyphrase-distribution` | Density (0.5–2.5%) and occurrence in key areas |
| `checkHeadings(input)` | `heading-structure`, `heading-keyphrase` | H1 presence, hierarchy, keyphrase in subheadings |
| `checkWordCount(input)` | `word-count` | Min 300 words (good at 1,000+) |
| `checkImages(input)` | `image-alt`, `image-keyphrase` | Alt text presence and keyphrase in alt |
| `checkLinks(input)` | `internal-links`, `external-links` | Internal and external link presence |
| Function | Check ID(s) | Checks For |
| ----------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `checkTitle(input)` | `title-presence`, `title-keyphrase` | Title presence **and** length (50–60 chars, validated inside `title-presence`), keyphrase |
| `checkMetaDescription(input)` | `meta-description-presence`, `meta-description-keyphrase` | Description presence, length (120–160 chars), keyphrase |
| `checkKeyphraseUsage(input)` | `keyphrase-density`, `keyphrase-distribution` | Density (0.5–2.5%) and occurrence in key areas |
| `checkHeadings(input)` | `heading-structure`, `heading-keyphrase` | H1 presence, hierarchy, keyphrase in subheadings |
| `checkWordCount(input)` | `word-count` | Min 300 words (good at 1,000+) |
| `checkImages(input)` | `image-alt`, `image-keyphrase` | Alt text presence and keyphrase in alt |
| `checkLinks(input)` | `internal-links`, `external-links` | Internal and external link presence |
> **Note:** There is no separate `title-length` check ID. Title length validation (50–60 chars) is evaluated inside the `title-presence` check — a title that exists but is outside the recommended range returns `status: 'ok'` rather than `'good'`.
### Types
| Type | Description |
| ----------------------- | --------------------------------------------------------- |
| `CheckId` | Union of all 13 built-in check IDs |
| `AnalysisConfig` | `{ disabledChecks?: CheckId[] }` |
| `AnalysisStatus` | `'good' \| 'needs-improvement' \| 'poor'` |
| `ContentAnalysisInput` | Input shape for `analyzeContent()` |
| `ContentAnalysisOutput` | Output shape from `analyzeContent()` |
| `AnalysisResult` | Single check result with id, status, message, score |
| Type | Description |
| ----------------------- | ------------------------------------------------------------------------ |
| `CheckId` | Union of all 13 built-in check IDs |
| `AnalysisConfig` | `{ disabledChecks?: CheckId[] }` |
| `AnalysisStatus` | `'good' \| 'ok' \| 'poor'` |
| `ContentAnalysisInput` | Input shape for `analyzeContent()` |
| `ContentAnalysisOutput` | Output shape from `analyzeContent()` |
| `AnalysisResult` | Single check result with id, title, description, status, score, maxScore |

@@ -345,3 +352,3 @@ ---

- **Pure TypeScript** — no compiled binary, no native modules
- **Zero runtime dependencies** — only `@power-seo/core` as a peer dependency
- **Zero external runtime dependencies** — `@power-seo/core` ships as a direct dependency, no separate install
- **Framework-agnostic** — works in any JavaScript environment

@@ -348,0 +355,0 @@ - **SSR compatible** — safe to run in Next.js Server Components, Remix loaders, or Express handlers