@ijonis/geo-lint 0.1.4 → 0.2.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 +47 -0
- package/README.md +58 -7
- package/dist/cli.cjs +195 -8
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +195 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +195 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +195 -8
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.d.cts
CHANGED
|
@@ -161,6 +161,10 @@ interface GeoConfig {
|
|
|
161
161
|
genericAuthorNames?: string[];
|
|
162
162
|
/** MDX component tags allowed in markdown (not flagged by inline-html rule) */
|
|
163
163
|
allowedHtmlTags?: string[];
|
|
164
|
+
/** Organization sameAs URLs for entity verification (LinkedIn, GitHub, Wikidata QID, etc.) */
|
|
165
|
+
organizationSameAs?: string[];
|
|
166
|
+
/** URL patterns identifying service pages (e.g. ['/services/', '/leistungen/']) */
|
|
167
|
+
servicePagePatterns?: string[];
|
|
164
168
|
}
|
|
165
169
|
/** Internationalization configuration */
|
|
166
170
|
interface I18nConfig {
|
|
@@ -169,6 +173,13 @@ interface I18nConfig {
|
|
|
169
173
|
/** Default locale — maps to hreflang x-default. Must be in locales array */
|
|
170
174
|
defaultLocale: string;
|
|
171
175
|
}
|
|
176
|
+
/** Technical site-level configuration */
|
|
177
|
+
interface TechnicalConfig {
|
|
178
|
+
/** Declared feed URLs (e.g. ['/feed.xml', '/rss.xml']) */
|
|
179
|
+
feedUrls?: string[];
|
|
180
|
+
/** Path to llms.txt file (e.g. '/llms.txt') */
|
|
181
|
+
llmsTxtUrl?: string;
|
|
182
|
+
}
|
|
172
183
|
/** User-facing configuration (partial, with defaults applied) */
|
|
173
184
|
interface GeoLintUserConfig {
|
|
174
185
|
/** Canonical site URL, e.g. 'https://example.com' (required) */
|
|
@@ -189,6 +200,8 @@ interface GeoLintUserConfig {
|
|
|
189
200
|
geo?: Partial<GeoConfig>;
|
|
190
201
|
/** Internationalization configuration */
|
|
191
202
|
i18n?: Partial<I18nConfig>;
|
|
203
|
+
/** Technical site-level configuration */
|
|
204
|
+
technical?: Partial<TechnicalConfig>;
|
|
192
205
|
/** Rule severity overrides: 'off' disables a rule */
|
|
193
206
|
rules?: Record<string, 'error' | 'warning' | 'off'>;
|
|
194
207
|
/** Threshold overrides */
|
|
@@ -227,6 +240,8 @@ interface GeoLintConfig {
|
|
|
227
240
|
excludeCategories: string[];
|
|
228
241
|
geo: GeoConfig;
|
|
229
242
|
i18n: I18nConfig;
|
|
243
|
+
/** Technical site-level configuration */
|
|
244
|
+
technical: TechnicalConfig;
|
|
230
245
|
rules: Record<string, 'error' | 'warning' | 'off'>;
|
|
231
246
|
thresholds: ThresholdConfig;
|
|
232
247
|
}
|
|
@@ -299,4 +314,4 @@ declare function lint(options?: LintOptions): Promise<number>;
|
|
|
299
314
|
*/
|
|
300
315
|
declare function lintQuiet(options?: LintOptions): Promise<LintResult[]>;
|
|
301
316
|
|
|
302
|
-
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type I18nConfig, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|
|
317
|
+
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type I18nConfig, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type TechnicalConfig, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|
package/dist/index.d.ts
CHANGED
|
@@ -161,6 +161,10 @@ interface GeoConfig {
|
|
|
161
161
|
genericAuthorNames?: string[];
|
|
162
162
|
/** MDX component tags allowed in markdown (not flagged by inline-html rule) */
|
|
163
163
|
allowedHtmlTags?: string[];
|
|
164
|
+
/** Organization sameAs URLs for entity verification (LinkedIn, GitHub, Wikidata QID, etc.) */
|
|
165
|
+
organizationSameAs?: string[];
|
|
166
|
+
/** URL patterns identifying service pages (e.g. ['/services/', '/leistungen/']) */
|
|
167
|
+
servicePagePatterns?: string[];
|
|
164
168
|
}
|
|
165
169
|
/** Internationalization configuration */
|
|
166
170
|
interface I18nConfig {
|
|
@@ -169,6 +173,13 @@ interface I18nConfig {
|
|
|
169
173
|
/** Default locale — maps to hreflang x-default. Must be in locales array */
|
|
170
174
|
defaultLocale: string;
|
|
171
175
|
}
|
|
176
|
+
/** Technical site-level configuration */
|
|
177
|
+
interface TechnicalConfig {
|
|
178
|
+
/** Declared feed URLs (e.g. ['/feed.xml', '/rss.xml']) */
|
|
179
|
+
feedUrls?: string[];
|
|
180
|
+
/** Path to llms.txt file (e.g. '/llms.txt') */
|
|
181
|
+
llmsTxtUrl?: string;
|
|
182
|
+
}
|
|
172
183
|
/** User-facing configuration (partial, with defaults applied) */
|
|
173
184
|
interface GeoLintUserConfig {
|
|
174
185
|
/** Canonical site URL, e.g. 'https://example.com' (required) */
|
|
@@ -189,6 +200,8 @@ interface GeoLintUserConfig {
|
|
|
189
200
|
geo?: Partial<GeoConfig>;
|
|
190
201
|
/** Internationalization configuration */
|
|
191
202
|
i18n?: Partial<I18nConfig>;
|
|
203
|
+
/** Technical site-level configuration */
|
|
204
|
+
technical?: Partial<TechnicalConfig>;
|
|
192
205
|
/** Rule severity overrides: 'off' disables a rule */
|
|
193
206
|
rules?: Record<string, 'error' | 'warning' | 'off'>;
|
|
194
207
|
/** Threshold overrides */
|
|
@@ -227,6 +240,8 @@ interface GeoLintConfig {
|
|
|
227
240
|
excludeCategories: string[];
|
|
228
241
|
geo: GeoConfig;
|
|
229
242
|
i18n: I18nConfig;
|
|
243
|
+
/** Technical site-level configuration */
|
|
244
|
+
technical: TechnicalConfig;
|
|
230
245
|
rules: Record<string, 'error' | 'warning' | 'off'>;
|
|
231
246
|
thresholds: ThresholdConfig;
|
|
232
247
|
}
|
|
@@ -299,4 +314,4 @@ declare function lint(options?: LintOptions): Promise<number>;
|
|
|
299
314
|
*/
|
|
300
315
|
declare function lintQuiet(options?: LintOptions): Promise<LintResult[]>;
|
|
301
316
|
|
|
302
|
-
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type I18nConfig, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|
|
317
|
+
export { type ContentAdapter, type ContentItem, type ContentPathConfig, type ContentType, type ExtractedImage, type ExtractedLink, type GeoConfig, type GeoLintConfig, type GeoLintUserConfig, type GlobalRule, type Heading, type I18nConfig, type LintOptions, type LintResult, type Rule, type RuleContext, type Severity, type TechnicalConfig, type ThresholdConfig, createAdapter, defineConfig, lint, lintQuiet, loadContentItems };
|
package/dist/index.js
CHANGED
|
@@ -147,7 +147,13 @@ var DEFAULT_CONFIG = {
|
|
|
147
147
|
"Tabs",
|
|
148
148
|
"Tab"
|
|
149
149
|
],
|
|
150
|
-
enabledContentTypes: ["blog"]
|
|
150
|
+
enabledContentTypes: ["blog"],
|
|
151
|
+
organizationSameAs: [],
|
|
152
|
+
servicePagePatterns: []
|
|
153
|
+
},
|
|
154
|
+
technical: {
|
|
155
|
+
feedUrls: [],
|
|
156
|
+
llmsTxtUrl: ""
|
|
151
157
|
},
|
|
152
158
|
i18n: {
|
|
153
159
|
locales: ["de", "en"],
|
|
@@ -222,12 +228,18 @@ function mergeWithDefaults(user) {
|
|
|
222
228
|
acronymAllowlist: user.geo?.acronymAllowlist ?? DEFAULT_CONFIG.geo.acronymAllowlist,
|
|
223
229
|
vagueHeadings: user.geo?.vagueHeadings ?? DEFAULT_CONFIG.geo.vagueHeadings,
|
|
224
230
|
genericAuthorNames: user.geo?.genericAuthorNames ?? DEFAULT_CONFIG.geo.genericAuthorNames,
|
|
225
|
-
allowedHtmlTags: user.geo?.allowedHtmlTags ?? DEFAULT_CONFIG.geo.allowedHtmlTags
|
|
231
|
+
allowedHtmlTags: user.geo?.allowedHtmlTags ?? DEFAULT_CONFIG.geo.allowedHtmlTags,
|
|
232
|
+
organizationSameAs: user.geo?.organizationSameAs ?? DEFAULT_CONFIG.geo.organizationSameAs,
|
|
233
|
+
servicePagePatterns: user.geo?.servicePagePatterns ?? DEFAULT_CONFIG.geo.servicePagePatterns
|
|
226
234
|
},
|
|
227
235
|
i18n: {
|
|
228
236
|
locales: user.i18n?.locales ?? DEFAULT_CONFIG.i18n.locales,
|
|
229
237
|
defaultLocale: user.i18n?.defaultLocale ?? DEFAULT_CONFIG.i18n.defaultLocale
|
|
230
238
|
},
|
|
239
|
+
technical: {
|
|
240
|
+
feedUrls: user.technical?.feedUrls ?? DEFAULT_CONFIG.technical.feedUrls,
|
|
241
|
+
llmsTxtUrl: user.technical?.llmsTxtUrl ?? DEFAULT_CONFIG.technical.llmsTxtUrl
|
|
242
|
+
},
|
|
231
243
|
rules: { ...DEFAULT_CONFIG.rules, ...user.rules ?? {} },
|
|
232
244
|
thresholds: {
|
|
233
245
|
title: { ...DEFAULT_CONFIG.thresholds.title, ...user.thresholds?.title ?? {} },
|
|
@@ -2578,12 +2590,72 @@ var datasetSchemaReadiness = {
|
|
|
2578
2590
|
return results;
|
|
2579
2591
|
}
|
|
2580
2592
|
};
|
|
2581
|
-
var
|
|
2593
|
+
var MIN_SAMEAS_ENTRIES = 2;
|
|
2594
|
+
function createSchemaSameAsRule(organizationSameAs) {
|
|
2595
|
+
let hasFired = false;
|
|
2596
|
+
return {
|
|
2597
|
+
name: "seo-schema-sameas-incomplete",
|
|
2598
|
+
severity: "warning",
|
|
2599
|
+
category: "seo",
|
|
2600
|
+
fixStrategy: "Add social profiles (LinkedIn, GitHub, Twitter), Wikidata QID, and Crunchbase URL to Organization schema sameAs array",
|
|
2601
|
+
run: (_item) => {
|
|
2602
|
+
if (hasFired) return [];
|
|
2603
|
+
hasFired = true;
|
|
2604
|
+
if (!organizationSameAs || organizationSameAs.length === 0) return [];
|
|
2605
|
+
if (organizationSameAs.length < MIN_SAMEAS_ENTRIES) {
|
|
2606
|
+
return [
|
|
2607
|
+
{
|
|
2608
|
+
file: "_site",
|
|
2609
|
+
field: "schema",
|
|
2610
|
+
rule: "seo-schema-sameas-incomplete",
|
|
2611
|
+
severity: "warning",
|
|
2612
|
+
message: `Organization sameAs has ${organizationSameAs.length} entry \u2014 include at least ${MIN_SAMEAS_ENTRIES} for entity verification`,
|
|
2613
|
+
suggestion: "AI models use sameAs to verify entity identity. Include at least LinkedIn + one other profile (GitHub, Wikidata QID, Crunchbase)."
|
|
2614
|
+
}
|
|
2615
|
+
];
|
|
2616
|
+
}
|
|
2617
|
+
return [];
|
|
2618
|
+
}
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
function createServicePageSchemaRule(servicePagePatterns) {
|
|
2622
|
+
return {
|
|
2623
|
+
name: "seo-service-page-no-schema",
|
|
2624
|
+
severity: "warning",
|
|
2625
|
+
category: "seo",
|
|
2626
|
+
fixStrategy: "Add Service structured data (JSON-LD) to service pages with name, description, provider, and areaServed.",
|
|
2627
|
+
run: (item) => {
|
|
2628
|
+
if (!servicePagePatterns || servicePagePatterns.length === 0) return [];
|
|
2629
|
+
const matchesPattern = servicePagePatterns.some(
|
|
2630
|
+
(pattern) => item.permalink.includes(pattern)
|
|
2631
|
+
);
|
|
2632
|
+
if (!matchesPattern) return [];
|
|
2633
|
+
return [
|
|
2634
|
+
{
|
|
2635
|
+
file: getDisplayPath(item),
|
|
2636
|
+
field: "schema",
|
|
2637
|
+
rule: "seo-service-page-no-schema",
|
|
2638
|
+
severity: "warning",
|
|
2639
|
+
message: `Service page "${item.permalink}" should have Service structured data`,
|
|
2640
|
+
suggestion: 'Service pages need schema markup to appear in AI answers for "[service] provider in [city]" queries. Add Service JSON-LD with name, description, provider, and areaServed.'
|
|
2641
|
+
}
|
|
2642
|
+
];
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
var schemaStaticRules = [
|
|
2582
2647
|
blogMissingSchemaFields,
|
|
2583
2648
|
faqpageSchemaReadiness,
|
|
2584
2649
|
breadcrumblistSchemaReadiness,
|
|
2585
2650
|
datasetSchemaReadiness
|
|
2586
2651
|
];
|
|
2652
|
+
function createSchemaRules(geo) {
|
|
2653
|
+
return [
|
|
2654
|
+
...schemaStaticRules,
|
|
2655
|
+
createSchemaSameAsRule(geo.organizationSameAs),
|
|
2656
|
+
createServicePageSchemaRule(geo.servicePagePatterns)
|
|
2657
|
+
];
|
|
2658
|
+
}
|
|
2587
2659
|
|
|
2588
2660
|
// src/rules/keyword-coherence-rules.ts
|
|
2589
2661
|
var MIN_SIGNIFICANT_WORDS = 2;
|
|
@@ -14615,6 +14687,58 @@ var geoMissingTldr = {
|
|
|
14615
14687
|
return [];
|
|
14616
14688
|
}
|
|
14617
14689
|
};
|
|
14690
|
+
var ORG_AUTHOR_PATTERNS = [
|
|
14691
|
+
/\bteam\b/i,
|
|
14692
|
+
/\bredaktion\b/i,
|
|
14693
|
+
/\beditorial\b/i,
|
|
14694
|
+
/\beditors?\b/i,
|
|
14695
|
+
/\bherausgeber\b/i,
|
|
14696
|
+
/\bverlag\b/i,
|
|
14697
|
+
/\bredaktionsteam\b/i
|
|
14698
|
+
];
|
|
14699
|
+
function createGeoAuthorNotPersonRule(brandName) {
|
|
14700
|
+
return {
|
|
14701
|
+
name: "geo-author-not-person",
|
|
14702
|
+
severity: "warning",
|
|
14703
|
+
category: "geo",
|
|
14704
|
+
fixStrategy: "Replace organization name with individual author name. Use Person type in BlogPosting schema for stronger E-E-A-T signals.",
|
|
14705
|
+
run: (item, context) => {
|
|
14706
|
+
const geoTypes = context.geoEnabledContentTypes ?? ["blog"];
|
|
14707
|
+
if (!geoTypes.includes(item.contentType)) return [];
|
|
14708
|
+
if (!item.author || item.author.trim() === "") return [];
|
|
14709
|
+
if (!brandName || brandName.trim() === "") return [];
|
|
14710
|
+
const normalizedAuthor = item.author.trim().toLowerCase();
|
|
14711
|
+
if (normalizedAuthor === brandName.trim().toLowerCase()) {
|
|
14712
|
+
return [
|
|
14713
|
+
{
|
|
14714
|
+
file: getDisplayPath(item),
|
|
14715
|
+
field: "author",
|
|
14716
|
+
rule: "geo-author-not-person",
|
|
14717
|
+
severity: "warning",
|
|
14718
|
+
message: `Author "${item.author}" is the organization name \u2014 use a person's name instead`,
|
|
14719
|
+
suggestion: "AI models cite named experts over faceless organizations. Use the actual author's name for stronger E-E-A-T signals."
|
|
14720
|
+
}
|
|
14721
|
+
];
|
|
14722
|
+
}
|
|
14723
|
+
const matchesOrgPattern = ORG_AUTHOR_PATTERNS.some(
|
|
14724
|
+
(pattern) => pattern.test(item.author)
|
|
14725
|
+
);
|
|
14726
|
+
if (matchesOrgPattern) {
|
|
14727
|
+
return [
|
|
14728
|
+
{
|
|
14729
|
+
file: getDisplayPath(item),
|
|
14730
|
+
field: "author",
|
|
14731
|
+
rule: "geo-author-not-person",
|
|
14732
|
+
severity: "warning",
|
|
14733
|
+
message: `Author "${item.author}" appears to be an organization or team name`,
|
|
14734
|
+
suggestion: "BlogPosting with author.@type: Person gets cited more than Organization. Use an individual person's name."
|
|
14735
|
+
}
|
|
14736
|
+
];
|
|
14737
|
+
}
|
|
14738
|
+
return [];
|
|
14739
|
+
}
|
|
14740
|
+
};
|
|
14741
|
+
}
|
|
14618
14742
|
var geoEeatStaticRules = [
|
|
14619
14743
|
geoMissingSourceCitations,
|
|
14620
14744
|
geoMissingExpertQuotes,
|
|
@@ -14627,7 +14751,8 @@ function createGeoEeatRules(geo) {
|
|
|
14627
14751
|
return [
|
|
14628
14752
|
...geoEeatStaticRules,
|
|
14629
14753
|
createGeoMissingAuthorRule(geo.genericAuthorNames ?? []),
|
|
14630
|
-
createGeoHeadingTooVagueRule(geo.vagueHeadings ?? [])
|
|
14754
|
+
createGeoHeadingTooVagueRule(geo.vagueHeadings ?? []),
|
|
14755
|
+
createGeoAuthorNotPersonRule(geo.brandName)
|
|
14631
14756
|
];
|
|
14632
14757
|
}
|
|
14633
14758
|
|
|
@@ -15616,6 +15741,68 @@ var contentQualityRules = [
|
|
|
15616
15741
|
sentenceVariety
|
|
15617
15742
|
];
|
|
15618
15743
|
|
|
15744
|
+
// src/rules/technical-site-rules.ts
|
|
15745
|
+
function createNoFeedRule(feedUrls) {
|
|
15746
|
+
let hasFired = false;
|
|
15747
|
+
return {
|
|
15748
|
+
name: "technical-no-feed",
|
|
15749
|
+
severity: "warning",
|
|
15750
|
+
category: "technical",
|
|
15751
|
+
fixStrategy: "Add an RSS or JSON feed endpoint exposing blog posts with full content.",
|
|
15752
|
+
run: (_item, _context) => {
|
|
15753
|
+
if (hasFired) return [];
|
|
15754
|
+
hasFired = true;
|
|
15755
|
+
if (feedUrls === void 0) return [];
|
|
15756
|
+
if (feedUrls.length === 0) {
|
|
15757
|
+
return [
|
|
15758
|
+
{
|
|
15759
|
+
file: "_site",
|
|
15760
|
+
field: "feed",
|
|
15761
|
+
rule: "technical-no-feed",
|
|
15762
|
+
severity: "warning",
|
|
15763
|
+
message: "No RSS/Atom/JSON feed detected \u2014 AI systems lose a structured ingestion path",
|
|
15764
|
+
suggestion: "Feeds provide a structured ingestion path for AI systems beyond crawler discovery. Add an RSS or JSON feed endpoint."
|
|
15765
|
+
}
|
|
15766
|
+
];
|
|
15767
|
+
}
|
|
15768
|
+
return [];
|
|
15769
|
+
}
|
|
15770
|
+
};
|
|
15771
|
+
}
|
|
15772
|
+
function createNoLlmsTxtRule(llmsTxtUrl) {
|
|
15773
|
+
let hasFired = false;
|
|
15774
|
+
return {
|
|
15775
|
+
name: "technical-no-llms-txt",
|
|
15776
|
+
severity: "warning",
|
|
15777
|
+
category: "technical",
|
|
15778
|
+
fixStrategy: "Create a /llms.txt endpoint that maps your most important content for LLM consumption in Markdown format.",
|
|
15779
|
+
run: (_item, _context) => {
|
|
15780
|
+
if (hasFired) return [];
|
|
15781
|
+
hasFired = true;
|
|
15782
|
+
if (llmsTxtUrl === void 0) return [];
|
|
15783
|
+
if (llmsTxtUrl.trim() === "") {
|
|
15784
|
+
return [
|
|
15785
|
+
{
|
|
15786
|
+
file: "_site",
|
|
15787
|
+
field: "llms-txt",
|
|
15788
|
+
rule: "technical-no-llms-txt",
|
|
15789
|
+
severity: "warning",
|
|
15790
|
+
message: "No /llms.txt endpoint detected \u2014 missing the emerging standard for LLM content declaration",
|
|
15791
|
+
suggestion: "llms.txt is the robots.txt equivalent for AI \u2014 trivial to add, future-proofs your site for LLM crawlers."
|
|
15792
|
+
}
|
|
15793
|
+
];
|
|
15794
|
+
}
|
|
15795
|
+
return [];
|
|
15796
|
+
}
|
|
15797
|
+
};
|
|
15798
|
+
}
|
|
15799
|
+
function createTechnicalSiteRules(technical) {
|
|
15800
|
+
return [
|
|
15801
|
+
createNoFeedRule(technical.feedUrls),
|
|
15802
|
+
createNoLlmsTxtRule(technical.llmsTxtUrl)
|
|
15803
|
+
];
|
|
15804
|
+
}
|
|
15805
|
+
|
|
15619
15806
|
// src/rules/index.ts
|
|
15620
15807
|
function buildRules(config, linkExtractor) {
|
|
15621
15808
|
const rules = [
|
|
@@ -15635,7 +15822,7 @@ function buildRules(config, linkExtractor) {
|
|
|
15635
15822
|
...createI18nRules(config.i18n),
|
|
15636
15823
|
...dateRules,
|
|
15637
15824
|
...config.categories.length > 0 ? createCategoryRules(config.categories) : [],
|
|
15638
|
-
...
|
|
15825
|
+
...createSchemaRules(config.geo),
|
|
15639
15826
|
...createGeoRules(config.geo),
|
|
15640
15827
|
...createGeoEeatRules(config.geo),
|
|
15641
15828
|
...geoStructureRules,
|
|
@@ -15643,7 +15830,8 @@ function buildRules(config, linkExtractor) {
|
|
|
15643
15830
|
...createGeoRagRules(config.geo),
|
|
15644
15831
|
...keywordCoherenceRules,
|
|
15645
15832
|
...createCanonicalRules(config.siteUrl),
|
|
15646
|
-
...contentQualityRules
|
|
15833
|
+
...contentQualityRules,
|
|
15834
|
+
...createTechnicalSiteRules(config.technical)
|
|
15647
15835
|
];
|
|
15648
15836
|
return rules.map((rule) => applyRuleOverride(rule, config.rules));
|
|
15649
15837
|
}
|
|
@@ -15832,8 +16020,7 @@ async function lint(options = {}) {
|
|
|
15832
16020
|
} else {
|
|
15833
16021
|
formatResults(results, lintableItems.length, excludedItems.length);
|
|
15834
16022
|
}
|
|
15835
|
-
|
|
15836
|
-
return errorCount > 0 ? 1 : 0;
|
|
16023
|
+
return 0;
|
|
15837
16024
|
}
|
|
15838
16025
|
async function lintQuiet(options = {}) {
|
|
15839
16026
|
const projectRoot = resolve2(options.projectRoot ?? process.cwd());
|