@ijonis/geo-lint 0.1.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/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +692 -0
- package/dist/cli.cjs +2716 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2689 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +2691 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +267 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.js +2646 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ijonis/geo-lint Configuration Types
|
|
3
|
+
*/
|
|
4
|
+
/** Content directory configuration */
|
|
5
|
+
interface ContentPathConfig {
|
|
6
|
+
/** Relative path from project root, e.g. 'content/blog' */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** Content type identifier */
|
|
9
|
+
type: 'blog' | 'page' | 'project';
|
|
10
|
+
/** URL prefix for permalink derivation, e.g. '/blog/' */
|
|
11
|
+
urlPrefix?: string;
|
|
12
|
+
/** Default locale when frontmatter has no locale field */
|
|
13
|
+
defaultLocale?: string;
|
|
14
|
+
}
|
|
15
|
+
/** GEO-specific configuration */
|
|
16
|
+
interface GeoConfig {
|
|
17
|
+
/** Brand name for entity density rule, e.g. 'ACME Corp'. Empty = skip check */
|
|
18
|
+
brandName: string;
|
|
19
|
+
/** Primary city/location for entity density rule, e.g. 'Berlin'. Empty = skip check */
|
|
20
|
+
brandCity: string;
|
|
21
|
+
/** Path to geo-keywords markdown file, relative to project root. Empty = skip */
|
|
22
|
+
keywordsPath: string;
|
|
23
|
+
}
|
|
24
|
+
/** User-facing configuration (partial, with defaults applied) */
|
|
25
|
+
interface GeoLintUserConfig {
|
|
26
|
+
/** Canonical site URL, e.g. 'https://example.com' (required) */
|
|
27
|
+
siteUrl: string;
|
|
28
|
+
/** Content directory configurations */
|
|
29
|
+
contentPaths?: ContentPathConfig[];
|
|
30
|
+
/** Static routes valid for link validation (e.g. ['/about', '/contact']) */
|
|
31
|
+
staticRoutes?: string[];
|
|
32
|
+
/** Image directories to scan, relative to project root */
|
|
33
|
+
imageDirectories?: string[];
|
|
34
|
+
/** Valid blog categories (empty = skip category-invalid rule) */
|
|
35
|
+
categories?: string[];
|
|
36
|
+
/** Slugs to fully exclude from linting */
|
|
37
|
+
excludeSlugs?: string[];
|
|
38
|
+
/** Content categories to fully exclude (e.g. 'legal') */
|
|
39
|
+
excludeCategories?: string[];
|
|
40
|
+
/** GEO-specific configuration */
|
|
41
|
+
geo?: Partial<GeoConfig>;
|
|
42
|
+
/** Rule severity overrides: 'off' disables a rule */
|
|
43
|
+
rules?: Record<string, 'error' | 'warning' | 'off'>;
|
|
44
|
+
/** Threshold overrides */
|
|
45
|
+
thresholds?: Partial<ThresholdConfig>;
|
|
46
|
+
}
|
|
47
|
+
/** Threshold configuration for validation rules */
|
|
48
|
+
interface ThresholdConfig {
|
|
49
|
+
title: {
|
|
50
|
+
minLength: number;
|
|
51
|
+
maxLength: number;
|
|
52
|
+
warnLength: number;
|
|
53
|
+
};
|
|
54
|
+
description: {
|
|
55
|
+
minLength: number;
|
|
56
|
+
maxLength: number;
|
|
57
|
+
warnLength: number;
|
|
58
|
+
};
|
|
59
|
+
slug: {
|
|
60
|
+
maxLength: number;
|
|
61
|
+
};
|
|
62
|
+
content: {
|
|
63
|
+
minWordCount: number;
|
|
64
|
+
minReadabilityScore: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Fully resolved configuration with all defaults applied */
|
|
68
|
+
interface GeoLintConfig {
|
|
69
|
+
siteUrl: string;
|
|
70
|
+
contentPaths: ContentPathConfig[];
|
|
71
|
+
staticRoutes: string[];
|
|
72
|
+
imageDirectories: string[];
|
|
73
|
+
categories: string[];
|
|
74
|
+
excludeSlugs: string[];
|
|
75
|
+
excludeCategories: string[];
|
|
76
|
+
geo: GeoConfig;
|
|
77
|
+
rules: Record<string, 'error' | 'warning' | 'off'>;
|
|
78
|
+
thresholds: ThresholdConfig;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @ijonis/geo-lint Core Types
|
|
83
|
+
* Shared interfaces for the SEO/GEO validation system
|
|
84
|
+
*/
|
|
85
|
+
type Severity = 'error' | 'warning';
|
|
86
|
+
type ContentType = 'blog' | 'page' | 'project';
|
|
87
|
+
/**
|
|
88
|
+
* Result of a single lint check
|
|
89
|
+
*/
|
|
90
|
+
interface LintResult {
|
|
91
|
+
/** Relative path: blog/my-post-slug */
|
|
92
|
+
file: string;
|
|
93
|
+
/** Field or area checked (e.g., 'title', 'body', 'links') */
|
|
94
|
+
field: string;
|
|
95
|
+
/** Rule identifier (e.g., 'title-too-long', 'geo-no-question-headings') */
|
|
96
|
+
rule: string;
|
|
97
|
+
/** Whether this blocks the build */
|
|
98
|
+
severity: Severity;
|
|
99
|
+
/** Human-readable error message */
|
|
100
|
+
message: string;
|
|
101
|
+
/** Optional actionable fix suggestion */
|
|
102
|
+
suggestion?: string;
|
|
103
|
+
/** Line number in MDX file if applicable */
|
|
104
|
+
line?: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Parsed content item combining frontmatter metadata and raw MDX body
|
|
108
|
+
*/
|
|
109
|
+
interface ContentItem {
|
|
110
|
+
title: string;
|
|
111
|
+
slug: string;
|
|
112
|
+
description: string;
|
|
113
|
+
permalink: string;
|
|
114
|
+
image?: string;
|
|
115
|
+
imageAlt?: string;
|
|
116
|
+
categories?: string[];
|
|
117
|
+
date?: string;
|
|
118
|
+
category?: string;
|
|
119
|
+
locale?: string;
|
|
120
|
+
translationKey?: string;
|
|
121
|
+
updatedAt?: string;
|
|
122
|
+
noindex?: boolean;
|
|
123
|
+
draft?: boolean;
|
|
124
|
+
/** Content type: blog, page, or project */
|
|
125
|
+
contentType: ContentType;
|
|
126
|
+
/** Full path to source file */
|
|
127
|
+
filePath: string;
|
|
128
|
+
/** Raw file content (frontmatter + body) */
|
|
129
|
+
rawContent: string;
|
|
130
|
+
/** Body content without frontmatter */
|
|
131
|
+
body: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Context passed to rules for cross-content validation
|
|
135
|
+
*/
|
|
136
|
+
interface RuleContext {
|
|
137
|
+
/** All content items (including excluded) for link/duplicate detection */
|
|
138
|
+
allContent: ContentItem[];
|
|
139
|
+
/** Set of all valid internal URL paths */
|
|
140
|
+
validSlugs: Set<string>;
|
|
141
|
+
/** Set of all valid image paths */
|
|
142
|
+
validImages: Set<string>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A lint rule definition
|
|
146
|
+
*/
|
|
147
|
+
interface Rule {
|
|
148
|
+
/** Unique rule identifier */
|
|
149
|
+
name: string;
|
|
150
|
+
/** Whether violations block the build */
|
|
151
|
+
severity: Severity;
|
|
152
|
+
/** Rule implementation */
|
|
153
|
+
run: (item: ContentItem, context: RuleContext) => LintResult[];
|
|
154
|
+
/** Machine-readable fix strategy for AI coding agents */
|
|
155
|
+
fixStrategy?: string;
|
|
156
|
+
/** Rule category for grouping (seo, geo, content, technical, i18n) */
|
|
157
|
+
category?: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A global rule that validates project-wide concerns (not per-item)
|
|
161
|
+
*/
|
|
162
|
+
interface GlobalRule {
|
|
163
|
+
/** Unique rule identifier */
|
|
164
|
+
name: string;
|
|
165
|
+
/** Whether violations block the build */
|
|
166
|
+
severity: Severity;
|
|
167
|
+
/** Rule implementation (no item — validates global state) */
|
|
168
|
+
run: () => LintResult[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extracted heading from MDX content
|
|
172
|
+
*/
|
|
173
|
+
interface Heading {
|
|
174
|
+
level: number;
|
|
175
|
+
text: string;
|
|
176
|
+
line: number;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Extracted link from MDX content
|
|
180
|
+
*/
|
|
181
|
+
interface ExtractedLink {
|
|
182
|
+
url: string;
|
|
183
|
+
text: string;
|
|
184
|
+
line: number;
|
|
185
|
+
isInternal: boolean;
|
|
186
|
+
originalUrl: string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Extracted image from MDX content
|
|
190
|
+
*/
|
|
191
|
+
interface ExtractedImage {
|
|
192
|
+
src: string;
|
|
193
|
+
alt: string;
|
|
194
|
+
line: number;
|
|
195
|
+
hasAlt: boolean;
|
|
196
|
+
source: 'frontmatter' | 'inline';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @ijonis/geo-lint Content Adapter Types
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Adapter interface for custom content sources.
|
|
205
|
+
* Implement this to integrate geo-lint with any CMS or content pipeline.
|
|
206
|
+
*/
|
|
207
|
+
interface ContentAdapter {
|
|
208
|
+
/** Load all content items from the source */
|
|
209
|
+
loadItems(projectRoot: string): ContentItem[] | Promise<ContentItem[]>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create an adapter from a simple function
|
|
213
|
+
*/
|
|
214
|
+
declare function createAdapter(fn: (projectRoot: string) => ContentItem[] | Promise<ContentItem[]>): ContentAdapter;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @ijonis/geo-lint Configuration Loader
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Helper for TypeScript-aware config files. Provides autocomplete in user configs.
|
|
222
|
+
*/
|
|
223
|
+
declare function defineConfig(config: GeoLintUserConfig): GeoLintUserConfig;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @ijonis/geo-lint MDX Content Adapter
|
|
227
|
+
* Scans directories for MDX/Markdown files and parses them with gray-matter
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Load all content items from configured content paths.
|
|
232
|
+
* Uses gray-matter for robust YAML frontmatter parsing.
|
|
233
|
+
*/
|
|
234
|
+
declare function loadContentItems(contentPaths: ContentPathConfig[], projectRoot: string): ContentItem[];
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @ijonis/geo-lint
|
|
238
|
+
* SEO and GEO (Generative Engine Optimization) linter for Markdown/MDX content
|
|
239
|
+
*
|
|
240
|
+
* The first open-source linter that checks your content for AI search visibility.
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/** Options for the lint() function */
|
|
244
|
+
interface LintOptions {
|
|
245
|
+
/** Project root directory (defaults to cwd) */
|
|
246
|
+
projectRoot?: string;
|
|
247
|
+
/** Explicit config (skips file loading) */
|
|
248
|
+
config?: GeoLintUserConfig;
|
|
249
|
+
/** Custom content adapter (skips default MDX scanning) */
|
|
250
|
+
adapter?: ContentAdapter;
|
|
251
|
+
/** Global rules to run after per-item rules */
|
|
252
|
+
globalRules?: GlobalRule[];
|
|
253
|
+
/** Output format: 'pretty' for terminal, 'json' for machine consumption */
|
|
254
|
+
format?: 'pretty' | 'json';
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Run the complete GEO lint process.
|
|
258
|
+
* Returns exit code: 0 for success (no errors), 1 for errors found.
|
|
259
|
+
*/
|
|
260
|
+
declare function lint(options?: LintOptions): Promise<number>;
|
|
261
|
+
/**
|
|
262
|
+
* Run the linter and return raw results (no console output).
|
|
263
|
+
* Useful for programmatic integration.
|
|
264
|
+
*/
|
|
265
|
+
declare function lintQuiet(options?: LintOptions): Promise<LintResult[]>;
|
|
266
|
+
|
|
267
|
+
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ijonis/geo-lint Configuration Types
|
|
3
|
+
*/
|
|
4
|
+
/** Content directory configuration */
|
|
5
|
+
interface ContentPathConfig {
|
|
6
|
+
/** Relative path from project root, e.g. 'content/blog' */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** Content type identifier */
|
|
9
|
+
type: 'blog' | 'page' | 'project';
|
|
10
|
+
/** URL prefix for permalink derivation, e.g. '/blog/' */
|
|
11
|
+
urlPrefix?: string;
|
|
12
|
+
/** Default locale when frontmatter has no locale field */
|
|
13
|
+
defaultLocale?: string;
|
|
14
|
+
}
|
|
15
|
+
/** GEO-specific configuration */
|
|
16
|
+
interface GeoConfig {
|
|
17
|
+
/** Brand name for entity density rule, e.g. 'ACME Corp'. Empty = skip check */
|
|
18
|
+
brandName: string;
|
|
19
|
+
/** Primary city/location for entity density rule, e.g. 'Berlin'. Empty = skip check */
|
|
20
|
+
brandCity: string;
|
|
21
|
+
/** Path to geo-keywords markdown file, relative to project root. Empty = skip */
|
|
22
|
+
keywordsPath: string;
|
|
23
|
+
}
|
|
24
|
+
/** User-facing configuration (partial, with defaults applied) */
|
|
25
|
+
interface GeoLintUserConfig {
|
|
26
|
+
/** Canonical site URL, e.g. 'https://example.com' (required) */
|
|
27
|
+
siteUrl: string;
|
|
28
|
+
/** Content directory configurations */
|
|
29
|
+
contentPaths?: ContentPathConfig[];
|
|
30
|
+
/** Static routes valid for link validation (e.g. ['/about', '/contact']) */
|
|
31
|
+
staticRoutes?: string[];
|
|
32
|
+
/** Image directories to scan, relative to project root */
|
|
33
|
+
imageDirectories?: string[];
|
|
34
|
+
/** Valid blog categories (empty = skip category-invalid rule) */
|
|
35
|
+
categories?: string[];
|
|
36
|
+
/** Slugs to fully exclude from linting */
|
|
37
|
+
excludeSlugs?: string[];
|
|
38
|
+
/** Content categories to fully exclude (e.g. 'legal') */
|
|
39
|
+
excludeCategories?: string[];
|
|
40
|
+
/** GEO-specific configuration */
|
|
41
|
+
geo?: Partial<GeoConfig>;
|
|
42
|
+
/** Rule severity overrides: 'off' disables a rule */
|
|
43
|
+
rules?: Record<string, 'error' | 'warning' | 'off'>;
|
|
44
|
+
/** Threshold overrides */
|
|
45
|
+
thresholds?: Partial<ThresholdConfig>;
|
|
46
|
+
}
|
|
47
|
+
/** Threshold configuration for validation rules */
|
|
48
|
+
interface ThresholdConfig {
|
|
49
|
+
title: {
|
|
50
|
+
minLength: number;
|
|
51
|
+
maxLength: number;
|
|
52
|
+
warnLength: number;
|
|
53
|
+
};
|
|
54
|
+
description: {
|
|
55
|
+
minLength: number;
|
|
56
|
+
maxLength: number;
|
|
57
|
+
warnLength: number;
|
|
58
|
+
};
|
|
59
|
+
slug: {
|
|
60
|
+
maxLength: number;
|
|
61
|
+
};
|
|
62
|
+
content: {
|
|
63
|
+
minWordCount: number;
|
|
64
|
+
minReadabilityScore: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Fully resolved configuration with all defaults applied */
|
|
68
|
+
interface GeoLintConfig {
|
|
69
|
+
siteUrl: string;
|
|
70
|
+
contentPaths: ContentPathConfig[];
|
|
71
|
+
staticRoutes: string[];
|
|
72
|
+
imageDirectories: string[];
|
|
73
|
+
categories: string[];
|
|
74
|
+
excludeSlugs: string[];
|
|
75
|
+
excludeCategories: string[];
|
|
76
|
+
geo: GeoConfig;
|
|
77
|
+
rules: Record<string, 'error' | 'warning' | 'off'>;
|
|
78
|
+
thresholds: ThresholdConfig;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @ijonis/geo-lint Core Types
|
|
83
|
+
* Shared interfaces for the SEO/GEO validation system
|
|
84
|
+
*/
|
|
85
|
+
type Severity = 'error' | 'warning';
|
|
86
|
+
type ContentType = 'blog' | 'page' | 'project';
|
|
87
|
+
/**
|
|
88
|
+
* Result of a single lint check
|
|
89
|
+
*/
|
|
90
|
+
interface LintResult {
|
|
91
|
+
/** Relative path: blog/my-post-slug */
|
|
92
|
+
file: string;
|
|
93
|
+
/** Field or area checked (e.g., 'title', 'body', 'links') */
|
|
94
|
+
field: string;
|
|
95
|
+
/** Rule identifier (e.g., 'title-too-long', 'geo-no-question-headings') */
|
|
96
|
+
rule: string;
|
|
97
|
+
/** Whether this blocks the build */
|
|
98
|
+
severity: Severity;
|
|
99
|
+
/** Human-readable error message */
|
|
100
|
+
message: string;
|
|
101
|
+
/** Optional actionable fix suggestion */
|
|
102
|
+
suggestion?: string;
|
|
103
|
+
/** Line number in MDX file if applicable */
|
|
104
|
+
line?: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Parsed content item combining frontmatter metadata and raw MDX body
|
|
108
|
+
*/
|
|
109
|
+
interface ContentItem {
|
|
110
|
+
title: string;
|
|
111
|
+
slug: string;
|
|
112
|
+
description: string;
|
|
113
|
+
permalink: string;
|
|
114
|
+
image?: string;
|
|
115
|
+
imageAlt?: string;
|
|
116
|
+
categories?: string[];
|
|
117
|
+
date?: string;
|
|
118
|
+
category?: string;
|
|
119
|
+
locale?: string;
|
|
120
|
+
translationKey?: string;
|
|
121
|
+
updatedAt?: string;
|
|
122
|
+
noindex?: boolean;
|
|
123
|
+
draft?: boolean;
|
|
124
|
+
/** Content type: blog, page, or project */
|
|
125
|
+
contentType: ContentType;
|
|
126
|
+
/** Full path to source file */
|
|
127
|
+
filePath: string;
|
|
128
|
+
/** Raw file content (frontmatter + body) */
|
|
129
|
+
rawContent: string;
|
|
130
|
+
/** Body content without frontmatter */
|
|
131
|
+
body: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Context passed to rules for cross-content validation
|
|
135
|
+
*/
|
|
136
|
+
interface RuleContext {
|
|
137
|
+
/** All content items (including excluded) for link/duplicate detection */
|
|
138
|
+
allContent: ContentItem[];
|
|
139
|
+
/** Set of all valid internal URL paths */
|
|
140
|
+
validSlugs: Set<string>;
|
|
141
|
+
/** Set of all valid image paths */
|
|
142
|
+
validImages: Set<string>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A lint rule definition
|
|
146
|
+
*/
|
|
147
|
+
interface Rule {
|
|
148
|
+
/** Unique rule identifier */
|
|
149
|
+
name: string;
|
|
150
|
+
/** Whether violations block the build */
|
|
151
|
+
severity: Severity;
|
|
152
|
+
/** Rule implementation */
|
|
153
|
+
run: (item: ContentItem, context: RuleContext) => LintResult[];
|
|
154
|
+
/** Machine-readable fix strategy for AI coding agents */
|
|
155
|
+
fixStrategy?: string;
|
|
156
|
+
/** Rule category for grouping (seo, geo, content, technical, i18n) */
|
|
157
|
+
category?: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A global rule that validates project-wide concerns (not per-item)
|
|
161
|
+
*/
|
|
162
|
+
interface GlobalRule {
|
|
163
|
+
/** Unique rule identifier */
|
|
164
|
+
name: string;
|
|
165
|
+
/** Whether violations block the build */
|
|
166
|
+
severity: Severity;
|
|
167
|
+
/** Rule implementation (no item — validates global state) */
|
|
168
|
+
run: () => LintResult[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extracted heading from MDX content
|
|
172
|
+
*/
|
|
173
|
+
interface Heading {
|
|
174
|
+
level: number;
|
|
175
|
+
text: string;
|
|
176
|
+
line: number;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Extracted link from MDX content
|
|
180
|
+
*/
|
|
181
|
+
interface ExtractedLink {
|
|
182
|
+
url: string;
|
|
183
|
+
text: string;
|
|
184
|
+
line: number;
|
|
185
|
+
isInternal: boolean;
|
|
186
|
+
originalUrl: string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Extracted image from MDX content
|
|
190
|
+
*/
|
|
191
|
+
interface ExtractedImage {
|
|
192
|
+
src: string;
|
|
193
|
+
alt: string;
|
|
194
|
+
line: number;
|
|
195
|
+
hasAlt: boolean;
|
|
196
|
+
source: 'frontmatter' | 'inline';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @ijonis/geo-lint Content Adapter Types
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Adapter interface for custom content sources.
|
|
205
|
+
* Implement this to integrate geo-lint with any CMS or content pipeline.
|
|
206
|
+
*/
|
|
207
|
+
interface ContentAdapter {
|
|
208
|
+
/** Load all content items from the source */
|
|
209
|
+
loadItems(projectRoot: string): ContentItem[] | Promise<ContentItem[]>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create an adapter from a simple function
|
|
213
|
+
*/
|
|
214
|
+
declare function createAdapter(fn: (projectRoot: string) => ContentItem[] | Promise<ContentItem[]>): ContentAdapter;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @ijonis/geo-lint Configuration Loader
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Helper for TypeScript-aware config files. Provides autocomplete in user configs.
|
|
222
|
+
*/
|
|
223
|
+
declare function defineConfig(config: GeoLintUserConfig): GeoLintUserConfig;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @ijonis/geo-lint MDX Content Adapter
|
|
227
|
+
* Scans directories for MDX/Markdown files and parses them with gray-matter
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Load all content items from configured content paths.
|
|
232
|
+
* Uses gray-matter for robust YAML frontmatter parsing.
|
|
233
|
+
*/
|
|
234
|
+
declare function loadContentItems(contentPaths: ContentPathConfig[], projectRoot: string): ContentItem[];
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @ijonis/geo-lint
|
|
238
|
+
* SEO and GEO (Generative Engine Optimization) linter for Markdown/MDX content
|
|
239
|
+
*
|
|
240
|
+
* The first open-source linter that checks your content for AI search visibility.
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/** Options for the lint() function */
|
|
244
|
+
interface LintOptions {
|
|
245
|
+
/** Project root directory (defaults to cwd) */
|
|
246
|
+
projectRoot?: string;
|
|
247
|
+
/** Explicit config (skips file loading) */
|
|
248
|
+
config?: GeoLintUserConfig;
|
|
249
|
+
/** Custom content adapter (skips default MDX scanning) */
|
|
250
|
+
adapter?: ContentAdapter;
|
|
251
|
+
/** Global rules to run after per-item rules */
|
|
252
|
+
globalRules?: GlobalRule[];
|
|
253
|
+
/** Output format: 'pretty' for terminal, 'json' for machine consumption */
|
|
254
|
+
format?: 'pretty' | 'json';
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Run the complete GEO lint process.
|
|
258
|
+
* Returns exit code: 0 for success (no errors), 1 for errors found.
|
|
259
|
+
*/
|
|
260
|
+
declare function lint(options?: LintOptions): Promise<number>;
|
|
261
|
+
/**
|
|
262
|
+
* Run the linter and return raw results (no console output).
|
|
263
|
+
* Useful for programmatic integration.
|
|
264
|
+
*/
|
|
265
|
+
declare function lintQuiet(options?: LintOptions): Promise<LintResult[]>;
|
|
266
|
+
|
|
267
|
+
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|