@momentumcms/plugins-seo 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +1665 -0
- package/index.js +1637 -0
- package/lib/seo-admin-routes.cjs +1377 -0
- package/lib/seo-admin-routes.js +1435 -0
- package/lib/seo-field-injector.cjs +312 -0
- package/lib/seo-field-injector.js +285 -0
- package/package.json +54 -0
- package/src/index.d.ts +11 -0
- package/src/lib/analysis/seo-analysis-collection.d.ts +7 -0
- package/src/lib/analysis/seo-analysis-hooks.d.ts +13 -0
- package/src/lib/analysis/seo-analysis.types.d.ts +56 -0
- package/src/lib/analysis/seo-analyzer.d.ts +36 -0
- package/src/lib/dashboard/seo-analysis-handler.d.ts +18 -0
- package/src/lib/meta/meta-builder.d.ts +14 -0
- package/src/lib/meta/meta-handler.d.ts +16 -0
- package/src/lib/robots/robots-handler.d.ts +27 -0
- package/src/lib/robots/robots-txt-generator.d.ts +12 -0
- package/src/lib/seo-admin-routes.d.ts +3 -0
- package/src/lib/seo-config.types.d.ts +174 -0
- package/src/lib/seo-field-injector.d.ts +27 -0
- package/src/lib/seo-fields.d.ts +21 -0
- package/src/lib/seo-plugin.d.ts +29 -0
- package/src/lib/seo-utils.d.ts +21 -0
- package/src/lib/settings/seo-settings-collection.d.ts +9 -0
- package/src/lib/settings/seo-settings-handler.d.ts +21 -0
- package/src/lib/sitemap/sitemap-cache.d.ts +12 -0
- package/src/lib/sitemap/sitemap-generator.d.ts +20 -0
- package/src/lib/sitemap/sitemap-handler.d.ts +28 -0
- package/src/lib/sitemap/sitemap-settings-collection.d.ts +7 -0
- package/src/lib/sitemap/sitemap-settings-handler.d.ts +18 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Analysis Result Types
|
|
3
|
+
*
|
|
4
|
+
* Types for the SEO scoring engine results.
|
|
5
|
+
*/
|
|
6
|
+
export interface SeoAnalysisResult {
|
|
7
|
+
/** Overall score (0-100) */
|
|
8
|
+
score: number;
|
|
9
|
+
/** Overall grade: 'good' (70+), 'warning' (40-69), 'poor' (0-39) */
|
|
10
|
+
grade: 'good' | 'warning' | 'poor';
|
|
11
|
+
/** Individual rule results */
|
|
12
|
+
rules: SeoRuleResult[];
|
|
13
|
+
/** ISO timestamp of when analysis was performed */
|
|
14
|
+
analyzedAt: string;
|
|
15
|
+
/** Collection slug */
|
|
16
|
+
collection: string;
|
|
17
|
+
/** Document ID */
|
|
18
|
+
documentId: string;
|
|
19
|
+
/** Focus keyword used for analysis */
|
|
20
|
+
focusKeyword?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SeoRuleResult {
|
|
23
|
+
/** Rule identifier */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Rule display name */
|
|
26
|
+
name: string;
|
|
27
|
+
/** Score for this rule (0-100) */
|
|
28
|
+
score: number;
|
|
29
|
+
/** Weight in total score */
|
|
30
|
+
weight: number;
|
|
31
|
+
/** Human-readable recommendation (null if passing) */
|
|
32
|
+
recommendation: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Structured meta tags returned by the meta tag API.
|
|
36
|
+
*/
|
|
37
|
+
export interface MetaTags {
|
|
38
|
+
/** The page title */
|
|
39
|
+
title: string;
|
|
40
|
+
/** Meta tags (name or property based) */
|
|
41
|
+
meta: Array<{
|
|
42
|
+
name?: string;
|
|
43
|
+
property?: string;
|
|
44
|
+
content: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** Link tags (canonical, etc.) */
|
|
47
|
+
link: Array<{
|
|
48
|
+
rel: string;
|
|
49
|
+
href: string;
|
|
50
|
+
}>;
|
|
51
|
+
/** Script tags (JSON-LD) */
|
|
52
|
+
script: Array<{
|
|
53
|
+
type: string;
|
|
54
|
+
innerHTML: string;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Analysis Engine
|
|
3
|
+
*
|
|
4
|
+
* Scoring engine with built-in rules for SEO content quality assessment.
|
|
5
|
+
*/
|
|
6
|
+
import type { SeoScoringRule, SeoScoringContext, SeoAnalysisConfig } from '../seo-config.types';
|
|
7
|
+
import type { SeoAnalysisResult } from './seo-analysis.types';
|
|
8
|
+
/**
|
|
9
|
+
* Strip HTML tags and return plain text.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractTextContent(doc: Record<string, unknown>): string;
|
|
12
|
+
/**
|
|
13
|
+
* Extract headings from HTML content in document fields.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractHeadings(doc: Record<string, unknown>): Array<{
|
|
16
|
+
level: number;
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function createTitleLengthRule(config?: SeoAnalysisConfig): SeoScoringRule;
|
|
20
|
+
export declare function createDescriptionLengthRule(config?: SeoAnalysisConfig): SeoScoringRule;
|
|
21
|
+
export declare function createKeywordInTitleRule(): SeoScoringRule;
|
|
22
|
+
export declare function createKeywordInDescriptionRule(): SeoScoringRule;
|
|
23
|
+
export declare function createKeywordDensityRule(config?: SeoAnalysisConfig): SeoScoringRule;
|
|
24
|
+
export declare function createHeadingStructureRule(): SeoScoringRule;
|
|
25
|
+
export declare function createOgDataRule(): SeoScoringRule;
|
|
26
|
+
export declare function createCanonicalUrlRule(): SeoScoringRule;
|
|
27
|
+
export declare function createContentLengthRule(): SeoScoringRule;
|
|
28
|
+
export declare function createNoIndexWarningRule(): SeoScoringRule;
|
|
29
|
+
/**
|
|
30
|
+
* Build the default set of scoring rules.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildDefaultRules(config?: SeoAnalysisConfig): SeoScoringRule[];
|
|
33
|
+
/**
|
|
34
|
+
* Run SEO analysis on a document.
|
|
35
|
+
*/
|
|
36
|
+
export declare function analyzeSeo(context: SeoScoringContext, rules: SeoScoringRule[]): Omit<SeoAnalysisResult, 'analyzedAt' | 'collection' | 'documentId' | 'focusKeyword'>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Analysis Dashboard Handler
|
|
3
|
+
*
|
|
4
|
+
* Read-only Express endpoint for the admin dashboard to fetch
|
|
5
|
+
* SEO analysis data. Requires admin authentication.
|
|
6
|
+
*
|
|
7
|
+
* The seo-analysis collection is `managed: true` (no auto-generated
|
|
8
|
+
* REST routes), so this handler provides the only HTTP read path.
|
|
9
|
+
*/
|
|
10
|
+
import type { Router } from 'express';
|
|
11
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
12
|
+
export interface DashboardHandlerOptions {
|
|
13
|
+
getApi: () => MomentumAPI | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates an Express router for the SEO dashboard read endpoint.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createDashboardRouter(options: DashboardHandlerOptions): Router;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Tag Builder
|
|
3
|
+
*
|
|
4
|
+
* Builds structured meta tag data from SEO field data.
|
|
5
|
+
* Includes Open Graph, Twitter Card, canonical, and JSON-LD support.
|
|
6
|
+
*/
|
|
7
|
+
import type { SeoFieldData } from '../seo-config.types';
|
|
8
|
+
import type { MetaTags } from '../analysis/seo-analysis.types';
|
|
9
|
+
/**
|
|
10
|
+
* Build structured meta tags from document SEO data.
|
|
11
|
+
*
|
|
12
|
+
* Falls back through: ogTitle → metaTitle → doc title field
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildMetaTags(doc: Record<string, unknown>, seo: SeoFieldData, _siteUrl?: string): MetaTags;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Tag API Express Handler
|
|
3
|
+
*
|
|
4
|
+
* Serves JSON meta tags at /meta/:collection/:id.
|
|
5
|
+
*/
|
|
6
|
+
import type { Router } from 'express';
|
|
7
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
8
|
+
export interface MetaHandlerOptions {
|
|
9
|
+
getApi: () => MomentumAPI | null;
|
|
10
|
+
siteUrl?: string;
|
|
11
|
+
seoCollections: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates an Express router for the meta tag API.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createMetaRouter(options: MetaHandlerOptions): Router;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robots.txt Express Handler
|
|
3
|
+
*
|
|
4
|
+
* Serves robots.txt based on plugin configuration.
|
|
5
|
+
* When `getApi` is provided, reads settings from the seo-settings
|
|
6
|
+
* managed collection; falls back to the static config.
|
|
7
|
+
*/
|
|
8
|
+
import type { Router } from 'express';
|
|
9
|
+
import type { RobotsConfig } from '../seo-config.types';
|
|
10
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
11
|
+
export { generateRobotsTxt } from './robots-txt-generator';
|
|
12
|
+
export interface RobotsHandlerOptions {
|
|
13
|
+
siteUrl: string;
|
|
14
|
+
config: RobotsConfig;
|
|
15
|
+
/** When provided, robots.txt is read from the seo-settings collection. */
|
|
16
|
+
getApi?: () => MomentumAPI | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates an Express router for the robots.txt endpoint.
|
|
20
|
+
*
|
|
21
|
+
* Returns `{ router, clearCache }` so the plugin can invalidate the
|
|
22
|
+
* cached response when settings change.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createRobotsRouter(options: RobotsHandlerOptions): {
|
|
25
|
+
router: Router;
|
|
26
|
+
clearCache: () => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robots.txt Generator (browser-safe)
|
|
3
|
+
*
|
|
4
|
+
* Pure function to generate robots.txt content from configuration.
|
|
5
|
+
* Separated from robots-handler.ts to avoid pulling Express into
|
|
6
|
+
* the browser bundle (admin pages import this for the live preview).
|
|
7
|
+
*/
|
|
8
|
+
import type { RobotsConfig } from '../seo-config.types';
|
|
9
|
+
/**
|
|
10
|
+
* Generate robots.txt content from configuration.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateRobotsTxt(siteUrl: string, config: RobotsConfig): string;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Plugin Configuration Types
|
|
3
|
+
*
|
|
4
|
+
* All configuration interfaces for the Momentum CMS SEO plugin.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* SEO Plugin Configuration.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* seoPlugin({
|
|
12
|
+
* collections: ['posts', 'pages'],
|
|
13
|
+
* siteUrl: 'https://example.com',
|
|
14
|
+
* analysis: { titleLength: { min: 40, max: 65 } },
|
|
15
|
+
* sitemap: { defaultPriority: 0.7 },
|
|
16
|
+
* robots: { crawlDelay: 1 },
|
|
17
|
+
* })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface SeoPluginConfig {
|
|
21
|
+
/** Enable/disable the SEO plugin. @default true */
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Collection slugs to inject SEO fields into.
|
|
25
|
+
* Pass `'*'` to inject into all collections (except managed and internal).
|
|
26
|
+
*/
|
|
27
|
+
collections: string[] | '*';
|
|
28
|
+
/**
|
|
29
|
+
* Collections to exclude from SEO field injection.
|
|
30
|
+
* Only effective when `collections` is `'*'`.
|
|
31
|
+
*/
|
|
32
|
+
excludeCollections?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Base URL for the site (used in sitemaps, canonical URLs, Open Graph).
|
|
35
|
+
* e.g., `'https://example.com'`
|
|
36
|
+
*/
|
|
37
|
+
siteUrl?: string;
|
|
38
|
+
/**
|
|
39
|
+
* SEO analysis configuration.
|
|
40
|
+
* - `true` (default): enable with default settings
|
|
41
|
+
* - `false`: disable analysis
|
|
42
|
+
* - object: custom thresholds and rules
|
|
43
|
+
*/
|
|
44
|
+
analysis?: boolean | SeoAnalysisConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Sitemap generation configuration.
|
|
47
|
+
* - `true` (default): enable with default settings
|
|
48
|
+
* - `false`: disable sitemap endpoint
|
|
49
|
+
* - object: custom sitemap settings
|
|
50
|
+
*/
|
|
51
|
+
sitemap?: boolean | SitemapConfig;
|
|
52
|
+
/**
|
|
53
|
+
* Robots.txt configuration.
|
|
54
|
+
* - `true` (default): enable with default settings
|
|
55
|
+
* - `false`: disable robots.txt endpoint
|
|
56
|
+
* - object: custom robots.txt rules
|
|
57
|
+
*/
|
|
58
|
+
robots?: boolean | RobotsConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Meta tag API configuration.
|
|
61
|
+
* - `true` (default): enable
|
|
62
|
+
* - `false`: disable
|
|
63
|
+
*/
|
|
64
|
+
metaApi?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Admin dashboard configuration.
|
|
67
|
+
* - `true` (default): enable built-in dashboard
|
|
68
|
+
* - `false`: disable
|
|
69
|
+
* - object: override loadComponent and/or group
|
|
70
|
+
*/
|
|
71
|
+
adminDashboard?: boolean | {
|
|
72
|
+
loadComponent?: unknown;
|
|
73
|
+
group?: string;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export interface SeoAnalysisConfig {
|
|
77
|
+
/** Run analysis asynchronously after save. @default true */
|
|
78
|
+
async?: boolean;
|
|
79
|
+
/** Custom scoring rules to add or override defaults */
|
|
80
|
+
rules?: SeoScoringRule[];
|
|
81
|
+
/** Title length thresholds */
|
|
82
|
+
titleLength?: {
|
|
83
|
+
min: number;
|
|
84
|
+
max: number;
|
|
85
|
+
};
|
|
86
|
+
/** Meta description length thresholds */
|
|
87
|
+
descriptionLength?: {
|
|
88
|
+
min: number;
|
|
89
|
+
max: number;
|
|
90
|
+
};
|
|
91
|
+
/** Target keyword density range (percentage) */
|
|
92
|
+
keywordDensity?: {
|
|
93
|
+
min: number;
|
|
94
|
+
max: number;
|
|
95
|
+
};
|
|
96
|
+
/** Collections to exclude from analysis */
|
|
97
|
+
excludeCollections?: string[];
|
|
98
|
+
}
|
|
99
|
+
export interface SeoScoringRule {
|
|
100
|
+
/** Unique rule identifier */
|
|
101
|
+
id: string;
|
|
102
|
+
/** Rule display name */
|
|
103
|
+
name: string;
|
|
104
|
+
/** Weight (0-100) in the total score */
|
|
105
|
+
weight: number;
|
|
106
|
+
/** Scoring function returning 0-100 */
|
|
107
|
+
score: (context: SeoScoringContext) => number;
|
|
108
|
+
/** Human-readable recommendation when score is low */
|
|
109
|
+
recommendation: (context: SeoScoringContext) => string | null;
|
|
110
|
+
}
|
|
111
|
+
export interface SeoScoringContext {
|
|
112
|
+
/** The SEO group field data */
|
|
113
|
+
seo: SeoFieldData;
|
|
114
|
+
/** The full document data */
|
|
115
|
+
doc: Record<string, unknown>;
|
|
116
|
+
/** The collection slug */
|
|
117
|
+
collection: string;
|
|
118
|
+
/** Extracted plain text from rich text fields */
|
|
119
|
+
textContent: string;
|
|
120
|
+
/** Extracted headings from rich text content */
|
|
121
|
+
headings: Array<{
|
|
122
|
+
level: number;
|
|
123
|
+
text: string;
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
126
|
+
export interface SeoFieldData {
|
|
127
|
+
metaTitle?: string;
|
|
128
|
+
metaDescription?: string;
|
|
129
|
+
canonicalUrl?: string;
|
|
130
|
+
focusKeyword?: string;
|
|
131
|
+
ogTitle?: string;
|
|
132
|
+
ogDescription?: string;
|
|
133
|
+
ogImage?: string | Record<string, unknown>;
|
|
134
|
+
ogType?: string;
|
|
135
|
+
twitterCard?: string;
|
|
136
|
+
noIndex?: boolean;
|
|
137
|
+
noFollow?: boolean;
|
|
138
|
+
excludeFromSitemap?: boolean;
|
|
139
|
+
structuredData?: unknown;
|
|
140
|
+
}
|
|
141
|
+
export type SitemapChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
142
|
+
export interface SitemapConfig {
|
|
143
|
+
/** Collections to include (default: all with SEO fields) */
|
|
144
|
+
includeCollections?: string[];
|
|
145
|
+
/** Collections to exclude */
|
|
146
|
+
excludeCollections?: string[];
|
|
147
|
+
/** Default priority for sitemap entries (0.0-1.0). @default 0.5 */
|
|
148
|
+
defaultPriority?: number;
|
|
149
|
+
/** Per-collection priority overrides */
|
|
150
|
+
priorities?: Record<string, number>;
|
|
151
|
+
/** Default change frequency. @default 'weekly' */
|
|
152
|
+
defaultChangeFreq?: SitemapChangeFreq;
|
|
153
|
+
/** Per-collection change frequency overrides */
|
|
154
|
+
changeFreqs?: Record<string, SitemapChangeFreq>;
|
|
155
|
+
/** Cache TTL in milliseconds. @default 300000 (5 minutes) */
|
|
156
|
+
cacheTtl?: number;
|
|
157
|
+
/** Maximum URLs per sitemap file (for sitemap index). @default 50000 */
|
|
158
|
+
maxUrlsPerSitemap?: number;
|
|
159
|
+
/** Custom URL builder per collection */
|
|
160
|
+
urlBuilder?: (collection: string, doc: Record<string, unknown>) => string | null;
|
|
161
|
+
}
|
|
162
|
+
export interface RobotsConfig {
|
|
163
|
+
/** User-agent rules */
|
|
164
|
+
rules?: RobotsRule[];
|
|
165
|
+
/** Additional sitemap URLs to include */
|
|
166
|
+
additionalSitemaps?: string[];
|
|
167
|
+
/** Crawl delay in seconds */
|
|
168
|
+
crawlDelay?: number;
|
|
169
|
+
}
|
|
170
|
+
export interface RobotsRule {
|
|
171
|
+
userAgent: string;
|
|
172
|
+
allow?: string[];
|
|
173
|
+
disallow?: string[];
|
|
174
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Field Injector
|
|
3
|
+
*
|
|
4
|
+
* Browser-safe — no server-side imports.
|
|
5
|
+
* Injects SEO fields as a named tab into targeted collections.
|
|
6
|
+
* Must be idempotent — called during both config generation and server init.
|
|
7
|
+
*/
|
|
8
|
+
import type { CollectionConfig } from '@momentumcms/core';
|
|
9
|
+
export interface SeoFieldInjectorOptions {
|
|
10
|
+
collections: string[] | '*';
|
|
11
|
+
excludeCollections?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Inject SEO fields as a named tab into targeted collections.
|
|
15
|
+
*
|
|
16
|
+
* - If the collection already has a top-level `tabs()` field, the SEO tab
|
|
17
|
+
* is appended to its tabs array.
|
|
18
|
+
* - Otherwise, all existing fields are wrapped in an unnamed "Content" tab
|
|
19
|
+
* and the SEO tab is added alongside it.
|
|
20
|
+
*
|
|
21
|
+
* Data model is unchanged: a named tab `{ name: 'seo' }` stores data
|
|
22
|
+
* at `doc.seo.*` — identical to the previous `group('seo', ...)`.
|
|
23
|
+
*
|
|
24
|
+
* @param collections - Mutable collections array
|
|
25
|
+
* @param options - Which collections to target
|
|
26
|
+
*/
|
|
27
|
+
export declare function injectSeoFields(collections: CollectionConfig[], options: SeoFieldInjectorOptions): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Field Definitions
|
|
3
|
+
*
|
|
4
|
+
* Browser-safe — no server-side imports.
|
|
5
|
+
* Creates the SEO fields that get injected into collections,
|
|
6
|
+
* either as a group field or as a named tab config.
|
|
7
|
+
*/
|
|
8
|
+
import type { Field, GroupField, TabConfig } from '@momentumcms/core';
|
|
9
|
+
/**
|
|
10
|
+
* Return the raw SEO fields shared by both group and tab config.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getSeoFields(): Field[];
|
|
13
|
+
/**
|
|
14
|
+
* Create the `seo` group field injected into collections.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSeoGroupField(): GroupField;
|
|
17
|
+
/**
|
|
18
|
+
* Create a named tab config for SEO fields.
|
|
19
|
+
* Named tabs store data under `doc.seo.*` — same shape as `group('seo', ...)`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createSeoTabConfig(): TabConfig;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Plugin
|
|
3
|
+
*
|
|
4
|
+
* A Momentum CMS plugin for SEO field injection, content analysis,
|
|
5
|
+
* sitemap generation, robots.txt, and meta tag API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { seoPlugin } from '@momentumcms/plugins/seo';
|
|
10
|
+
*
|
|
11
|
+
* export default defineMomentumConfig({
|
|
12
|
+
* plugins: [
|
|
13
|
+
* seoPlugin({
|
|
14
|
+
* collections: ['posts', 'pages'],
|
|
15
|
+
* siteUrl: 'https://example.com',
|
|
16
|
+
* }),
|
|
17
|
+
* ],
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { MomentumPlugin } from '@momentumcms/plugins/core';
|
|
22
|
+
import type { SeoPluginConfig } from './seo-config.types';
|
|
23
|
+
/**
|
|
24
|
+
* Creates an SEO plugin.
|
|
25
|
+
*
|
|
26
|
+
* @param config - SEO plugin configuration
|
|
27
|
+
* @returns Plugin instance
|
|
28
|
+
*/
|
|
29
|
+
export declare function seoPlugin(config: SeoPluginConfig): MomentumPlugin;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SEO Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* Common helpers used across multiple SEO plugin modules.
|
|
5
|
+
*/
|
|
6
|
+
import type { CollectionConfig } from '@momentumcms/core';
|
|
7
|
+
import type { SeoFieldData } from './seo-config.types';
|
|
8
|
+
/**
|
|
9
|
+
* Check if a collection already has SEO fields — either as a top-level
|
|
10
|
+
* `group('seo', ...)` or as a named `seo` tab inside a `tabs()` field.
|
|
11
|
+
*/
|
|
12
|
+
export declare function hasSeoField(collection: CollectionConfig): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Extract SEO field data from a document's `seo` property.
|
|
15
|
+
* Returns an empty object if the seo field is missing or malformed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractSeoFieldData(doc: Record<string, unknown>): SeoFieldData;
|
|
18
|
+
/**
|
|
19
|
+
* Compute SEO grade from a numeric score.
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeGrade(score: number): 'good' | 'warning' | 'poor';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Settings Collection (Singleton)
|
|
3
|
+
*
|
|
4
|
+
* Stores site-wide SEO configuration: robots.txt rules, crawl delay,
|
|
5
|
+
* additional sitemaps, and future global SEO settings.
|
|
6
|
+
* One document per site (upsert by querying for the singleton).
|
|
7
|
+
* Hidden from sidebar — managed via the Robots Settings admin page.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SeoSettings: import("@momentumcms/core").CollectionConfig;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Settings Express Handler
|
|
3
|
+
*
|
|
4
|
+
* GET/PUT endpoint for the site-wide SEO settings singleton.
|
|
5
|
+
* Requires admin authentication. The seo-settings collection is
|
|
6
|
+
* `managed: true`, so this handler provides the only HTTP path.
|
|
7
|
+
*/
|
|
8
|
+
import type { Router } from 'express';
|
|
9
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
10
|
+
import type { RobotsConfig } from '../seo-config.types';
|
|
11
|
+
export interface SeoSettingsHandlerOptions {
|
|
12
|
+
getApi: () => MomentumAPI | null;
|
|
13
|
+
/** Static robots config from plugin init — used as defaults. */
|
|
14
|
+
defaultRobotsConfig?: RobotsConfig;
|
|
15
|
+
/** Called when settings are saved — invalidate caches. */
|
|
16
|
+
onSettingsChanged?: () => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates an Express router for SEO settings CRUD (singleton).
|
|
20
|
+
*/
|
|
21
|
+
export declare function createSeoSettingsRouter(options: SeoSettingsHandlerOptions): Router;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory TTL cache for sitemap XML.
|
|
3
|
+
*/
|
|
4
|
+
export declare class SitemapCache {
|
|
5
|
+
private readonly store;
|
|
6
|
+
private readonly defaultTtl;
|
|
7
|
+
constructor(defaultTtl?: number);
|
|
8
|
+
get(key: string): string | null;
|
|
9
|
+
set(key: string, value: string, ttl?: number): void;
|
|
10
|
+
invalidate(key: string): void;
|
|
11
|
+
clear(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sitemap XML Generator
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that build XML sitemap and sitemap index strings.
|
|
5
|
+
*/
|
|
6
|
+
import type { SitemapChangeFreq } from '../seo-config.types';
|
|
7
|
+
export interface SitemapEntry {
|
|
8
|
+
loc: string;
|
|
9
|
+
lastmod?: string;
|
|
10
|
+
changefreq?: SitemapChangeFreq;
|
|
11
|
+
priority?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate an XML sitemap from a list of URL entries.
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateSitemap(entries: SitemapEntry[]): string;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a sitemap index pointing to multiple sitemap files.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateSitemapIndex(sitemapUrls: string[]): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sitemap Express Handler
|
|
3
|
+
*
|
|
4
|
+
* Serves XML sitemaps at /sitemap.xml with in-memory caching.
|
|
5
|
+
* Reads per-collection settings from the seo-sitemap-settings collection
|
|
6
|
+
* and per-document exclusions from the SEO field group.
|
|
7
|
+
*/
|
|
8
|
+
import type { Router } from 'express';
|
|
9
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
10
|
+
import type { SitemapConfig } from '../seo-config.types';
|
|
11
|
+
import { SitemapCache } from './sitemap-cache';
|
|
12
|
+
export interface SitemapHandlerOptions {
|
|
13
|
+
getApi: () => MomentumAPI | null;
|
|
14
|
+
siteUrl: string;
|
|
15
|
+
config: SitemapConfig;
|
|
16
|
+
seoCollections: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates an Express router for the sitemap endpoint.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createSitemapRouter(options: SitemapHandlerOptions): {
|
|
22
|
+
router: Router;
|
|
23
|
+
clearCache: () => void;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Expose cache for shutdown cleanup.
|
|
27
|
+
*/
|
|
28
|
+
export { SitemapCache };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Sitemap Settings Collection
|
|
3
|
+
*
|
|
4
|
+
* Stores per-collection sitemap configuration (include/exclude, priority, change frequency).
|
|
5
|
+
* Hidden from sidebar — managed via the Sitemap Settings admin page.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SeoSitemapSettings: import("@momentumcms/core").CollectionConfig;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sitemap Settings Express Handler
|
|
3
|
+
*
|
|
4
|
+
* CRUD endpoint for per-collection sitemap configuration.
|
|
5
|
+
* Requires admin authentication. The seo-sitemap-settings collection
|
|
6
|
+
* is `managed: true`, so this handler provides the only HTTP path.
|
|
7
|
+
*/
|
|
8
|
+
import type { Router } from 'express';
|
|
9
|
+
import type { MomentumAPI } from '@momentumcms/plugins/core';
|
|
10
|
+
export interface SitemapSettingsHandlerOptions {
|
|
11
|
+
getApi: () => MomentumAPI | null;
|
|
12
|
+
seoCollections: string[];
|
|
13
|
+
onSettingsChanged?: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates an Express router for sitemap settings CRUD.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createSitemapSettingsRouter(options: SitemapSettingsHandlerOptions): Router;
|