@jdevalk/astro-seo-graph 1.0.0 → 1.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/AGENTS.md CHANGED
@@ -2634,6 +2634,9 @@ export default defineConfig({
2634
2634
  // All options below are optional; listed with defaults.
2635
2635
  validateH1: true,
2636
2636
  validateUniqueMetadata: true,
2637
+ validateImageAlt: true,
2638
+ validateMetadataLength: true, // or { title: { max: 60 }, ... }
2639
+ validateInternalLinks: true, // or { skip: (href) => href.startsWith('/api/') }
2637
2640
  // indexNow: { ... },
2638
2641
  // llmsTxt: { ... },
2639
2642
  }),
@@ -2643,12 +2646,15 @@ export default defineConfig({
2643
2646
 
2644
2647
  ### Options
2645
2648
 
2646
- | Option | Default | Purpose |
2647
- | ------------------------ | ------- | --------------------------------------------------------------------- |
2648
- | `validateH1` | `true` | Warn when a page has zero or >1 `<h1>` elements. |
2649
- | `validateUniqueMetadata` | `true` | Warn when two pages share the same `<title>` or meta description. |
2650
- | `indexNow` | | Submit built URLs to IndexNow. Omit to disable. |
2651
- | `llmsTxt` | | Generate `llms.txt` at the root of the build output. Omit to disable. |
2649
+ | Option | Default | Purpose |
2650
+ | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
2651
+ | `validateH1` | `true` | Warn when a page has zero or >1 `<h1>` elements. |
2652
+ | `validateUniqueMetadata` | `true` | Warn when two pages share the same `<title>` or meta description. |
2653
+ | `validateImageAlt` | `true` | Warn when a page has `<img>` tags missing an `alt` attribute. |
2654
+ | `validateMetadataLength` | `true` | Warn when `<title>` or description length falls outside SERP-friendly bounds (title 30–65, description 70–200 by default; overridable per-field). |
2655
+ | `validateInternalLinks` | `true` | Warn on trailing-slash mismatches and links to pages not in the build. Accepts `{ skip }` to exclude SSR-only routes. |
2656
+ | `indexNow` | — | Submit built URLs to IndexNow. Omit to disable. |
2657
+ | `llmsTxt` | — | Generate `llms.txt` at the root of the build output. Omit to disable. |
2652
2658
 
2653
2659
  ### H1 and metadata validation
2654
2660
 
@@ -2656,15 +2662,57 @@ export default defineConfig({
2656
2662
  most common on-page SEO/accessibility miss. `validateUniqueMetadata`
2657
2663
  flags `<title>` or `<meta name="description">` values that repeat across
2658
2664
  pages; duplicates hurt Google's ability to pick a canonical result and
2659
- can only be spotted across the whole corpus.
2665
+ can only be spotted across the whole corpus. `validateImageAlt` flags
2666
+ `<img>` tags missing an `alt` attribute. WCAG-sanctioned decorative
2667
+ markers are respected: `alt=""` (the canonical pattern) and
2668
+ `role="presentation"`/`role="none"` (removes the image from the
2669
+ accessibility tree) are not flagged. Only a tag with neither triggers a
2670
+ warning.
2660
2671
 
2661
- Both extractors are exported for reuse:
2672
+ `validateMetadataLength` flags `<title>` and `<meta name="description">`
2673
+ values outside the configured bounds. Pass `true` for the defaults
2674
+ (title 30–65, description 70–200) or an object to override per-field:
2675
+
2676
+ ```ts
2677
+ validateMetadataLength: {
2678
+ title: { min: 40, max: 60 },
2679
+ description: { max: 160 },
2680
+ },
2681
+ ```
2682
+
2683
+ `validateInternalLinks` scans `<a href>` values across every built page
2684
+ and flags two classes of issue: trailing-slash mismatches (e.g. linking
2685
+ to `/about-me` when the built page is `/about-me/` — "works" via
2686
+ redirect but wastes a round-trip on every click) and true 404s (links
2687
+ to paths not in the build). Only same-origin (via `config.site`) and
2688
+ root-relative links are checked; external URLs, `mailto:`, `tel:`, and
2689
+ fragment-only links are skipped.
2690
+
2691
+ Explicit redirects are honored by default: sources in
2692
+ `public/_redirects` (Netlify / Cloudflare Pages format) and literal
2693
+ keys in Astro's `redirects` config are treated as valid link targets.
2694
+ Dynamic rules (`*`, `:splat`, `[slug]` params) are skipped — use
2695
+ `skip` for those cases. Set `honorRedirects: false` to opt out (useful
2696
+ when auditing for redirect hops).
2697
+
2698
+ Pass `{ skip: (href) => boolean }` to exclude SSR-only routes or paths
2699
+ handled at the host/CDN layer.
2700
+
2701
+ The extractors, helpers, and the length resolver are exported for reuse:
2662
2702
 
2663
2703
  ```ts
2664
2704
  import {
2705
+ classifyInternalLink,
2706
+ collectAstroRedirectSources,
2665
2707
  countH1s,
2708
+ DEFAULT_METADATA_LENGTH_BOUNDS,
2709
+ extractAnchorHrefs,
2666
2710
  extractTitle,
2667
2711
  extractMetaDescription,
2712
+ findImagesWithoutAlt,
2713
+ htmlFileToPath,
2714
+ parseNetlifyRedirects,
2715
+ resolveMetadataLengthBounds,
2668
2716
  } from '@jdevalk/astro-seo-graph/integration';
2669
2717
  ```
2670
2718
 
@@ -2678,13 +2726,17 @@ seoGraph({
2678
2726
  siteUrl: 'https://example.com',
2679
2727
  // keyLocation?: defaults to https://<host>/<key>.txt
2680
2728
  // endpoint?: defaults to api.indexnow.org
2681
- // filter?: (url) => boolean — drop URLs before submission
2729
+ // filter?: (url) => boolean — composed on top of the built-in
2730
+ // /404 exclusion. Example skipping paginated archives:
2731
+ // filter: (url) => !/^\/blog\/\d+\/$/.test(new URL(url).pathname),
2682
2732
  },
2683
2733
  });
2684
2734
  ```
2685
2735
 
2686
2736
  Only URLs on `host` are submitted. `index.html` paths are rewritten to
2687
- their trailing-slash form.
2737
+ their trailing-slash form. `/404` (and `/404/`) are always excluded —
2738
+ search engines don't need to be notified about the 404 page and
2739
+ submitting it wastes daily IndexNow quota.
2688
2740
 
2689
2741
  **Deploy the key file first.** IndexNow verifies ownership by fetching
2690
2742
  `https://<host>/<key>.txt` on every submission. Submissions sent before
package/README.md CHANGED
@@ -346,6 +346,15 @@ actions. Currently:
346
346
  common SEO and accessibility issue).
347
347
  - Warns about duplicate `<title>` or meta description values across the
348
348
  built corpus.
349
+ - Warns about `<img>` tags missing an `alt` attribute. Decorative
350
+ images marked with `alt=""` or `role="presentation"`/`role="none"`
351
+ are respected and not flagged; only a fully missing attribute is.
352
+ - Warns about `<title>` and meta description lengths outside
353
+ SERP-friendly bounds (defaults: title 30–65, description 70–200).
354
+ Bounds are configurable.
355
+ - Warns about internal `<a href>` links that point to a URL missing a
356
+ trailing slash (or carrying an extra one) relative to the built
357
+ page. Also flags links to paths that aren't in the build (true 404s).
349
358
  - Optionally submits built URLs to [IndexNow](https://www.indexnow.org)
350
359
  after the build completes.
351
360
  - Optionally generates an [`llms.txt`](https://llmstxt.org) file at the
@@ -371,17 +380,73 @@ export default defineConfig({
371
380
 
372
381
  Options:
373
382
 
374
- | Prop | Default | Description |
375
- | ------------------------ | ------- | ----------------------------------------------------------------- |
376
- | `validateH1` | `true` | Warn about pages without exactly one `<h1>` |
377
- | `validateUniqueMetadata` | `true` | Warn about duplicate `<title>` or meta description across pages |
378
- | `indexNow` | | Submit built URLs to IndexNow. See below for sub-options. |
379
- | `llmsTxt` | | Generate `llms.txt` at the build root. See below for sub-options. |
383
+ | Prop | Default | Description |
384
+ | ------------------------ | ------- | ------------------------------------------------------------------------------ |
385
+ | `validateH1` | `true` | Warn about pages without exactly one `<h1>` |
386
+ | `validateUniqueMetadata` | `true` | Warn about duplicate `<title>` or meta description across pages |
387
+ | `validateImageAlt` | `true` | Warn about `<img>` tags missing an `alt` attribute |
388
+ | `validateMetadataLength` | `true` | Warn when title or description length is outside configured bounds (see below) |
389
+ | `validateInternalLinks` | `true` | Warn on trailing-slash mismatches and links to pages not in the build |
390
+ | `indexNow` | — | Submit built URLs to IndexNow. See below for sub-options. |
391
+ | `llmsTxt` | — | Generate `llms.txt` at the build root. See below for sub-options. |
380
392
 
381
393
  `indexNow` sub-options: `key` (8–128 hex chars), `host` (bare host, e.g.
382
394
  `example.com`), `siteUrl` (absolute origin), `keyLocation?` (defaults to
383
395
  `https://<host>/<key>.txt`), `endpoint?` (defaults to `api.indexnow.org`),
384
- `filter?` (drop URLs for which the callback returns `false`).
396
+ `filter?` (drop URLs for which the callback returns `false`; composed on
397
+ top of the built-in `/404` exclusion).
398
+
399
+ `validateMetadataLength` accepts `true`/`false` for the defaults, or an
400
+ object to override bounds. Length is measured on the whitespace-collapsed,
401
+ entity-decoded text — the same thing Google renders in the SERP.
402
+
403
+ ```js
404
+ validateMetadataLength: {
405
+ title: { min: 40, max: 60 }, // tighter than the default 30–65
406
+ description: { max: 160 }, // keep description min at its default (70)
407
+ },
408
+ ```
409
+
410
+ `validateInternalLinks` scans every built page's `<a href>` values and
411
+ checks them against the set of paths actually produced by the build.
412
+ Catches the common "I linked to `/about-me` but the page is `/about-me/`"
413
+ bug (which "works" via redirect but wastes a round-trip). Only checks
414
+ same-origin links (via `config.site`) and root-relative `/foo`-style
415
+ hrefs; `mailto:`, `tel:`, fragment-only (`#x`), and off-origin URLs are
416
+ skipped.
417
+
418
+ Explicit redirects are honored as valid targets by default. Sources in
419
+ `public/_redirects` (Netlify / Cloudflare Pages format) and literal
420
+ keys in Astro's `redirects` config are unioned into the built-paths
421
+ set, so linking to a redirect source doesn't warn. Dynamic rules
422
+ (wildcards, splats, `[slug]` params) are skipped — glob matching is
423
+ out of scope; use `skip` for those cases. Set `honorRedirects: false`
424
+ to opt out — useful when you're actively eliminating redirect hops and
425
+ want to see every internal link that doesn't hit a built page
426
+ directly.
427
+
428
+ Pass a `skip` callback to exclude specific hrefs — useful for SSR-only
429
+ routes or paths handled at the host/CDN layer:
430
+
431
+ ```js
432
+ validateInternalLinks: {
433
+ skip: (href) => href.startsWith('/api/') || href === '/search',
434
+ },
435
+ ```
436
+
437
+ The `/404` page is always excluded — no one needs search engines notified
438
+ about it, and submitting it wastes daily IndexNow quota. Use `filter` to
439
+ exclude site-specific utility pages:
440
+
441
+ ```js
442
+ indexNow: {
443
+ key: process.env.INDEXNOW_KEY!,
444
+ host: 'example.com',
445
+ siteUrl: 'https://example.com',
446
+ // Skip paginated archives like /blog/2/, /videos/3/
447
+ filter: (url) => !/^\/(?:blog|videos)\/\d+\/$/.test(new URL(url).pathname),
448
+ },
449
+ ```
385
450
 
386
451
  `llmsTxt` sub-options: `title` (required H1), `siteUrl` (required, used to
387
452
  resolve crawled HTML paths), `summary?` (rendered as a blockquote),
@@ -7,9 +7,25 @@ interface BuildDoneHook {
7
7
  [key: string]: any;
8
8
  };
9
9
  }
10
+ /**
11
+ * Subset of `AstroConfig` we read in `astro:config:setup`. Only the
12
+ * fields we actually use, so the integration stays decoupled from
13
+ * Astro's full type surface.
14
+ */
15
+ interface ConfigSetupHook {
16
+ config: {
17
+ site?: string | URL;
18
+ trailingSlash?: 'always' | 'never' | 'ignore';
19
+ redirects?: Record<string, string | {
20
+ status?: number;
21
+ destination: string;
22
+ } | undefined>;
23
+ };
24
+ }
10
25
  interface AstroIntegrationLike {
11
26
  name: string;
12
27
  hooks: {
28
+ 'astro:config:setup'?: (args: ConfigSetupHook) => void | Promise<void>;
13
29
  'astro:build:done'?: (args: BuildDoneHook) => void | Promise<void>;
14
30
  };
15
31
  }
@@ -36,10 +52,27 @@ export interface IndexNowIntegrationOptions {
36
52
  endpoint?: string;
37
53
  /**
38
54
  * Filter the list of URLs before submission. Return `false` to skip
39
- * a URL. Useful for excluding 404 pages, drafts, etc.
55
+ * a URL. Composed on top of the built-in `/404` exclusion you
56
+ * don't need to re-exclude that yourself.
57
+ *
58
+ * @example excluding paginated archives in addition to the default 404:
59
+ * ```ts
60
+ * filter: (url) => !/^\/blog\/\d+\/$/.test(new URL(url).pathname),
61
+ * ```
40
62
  */
41
63
  filter?: (url: string) => boolean;
42
64
  }
65
+ /**
66
+ * Pathnames that `indexNow` excludes by default, regardless of any
67
+ * caller-supplied `filter`. The 404 page is the only universal case —
68
+ * search engines don't need to be notified about it, and submitting it
69
+ * wastes daily IndexNow quota.
70
+ *
71
+ * Matches both `/404` (from an Astro `src/pages/404.astro` → `404.html`
72
+ * that `htmlFileToUrl` strips to `/404`) and `/404/` (the directory form
73
+ * when the build emits `404/index.html`).
74
+ */
75
+ export declare function isDefaultExcludedFromIndexNow(url: string): boolean;
43
76
  export interface LlmsTxtIntegrationOptions {
44
77
  /** H1 of the generated `llms.txt`. */
45
78
  title: string;
@@ -71,6 +104,76 @@ export interface LlmsTxtIntegrationOptions {
71
104
  */
72
105
  outputPath?: string;
73
106
  }
107
+ /**
108
+ * Options for `validateInternalLinks`. Pass `true` to enable with
109
+ * defaults, `false` to disable, or an object to customize.
110
+ */
111
+ export interface ValidateInternalLinksOptions {
112
+ /**
113
+ * Skip a specific href from validation. Return `true` to exclude.
114
+ * Useful for SSR-only routes, dynamic redirects, or paths handled
115
+ * at the host/CDN layer that aren't present in the build output.
116
+ *
117
+ * Receives the raw `href` attribute value as it appeared in the
118
+ * HTML (not resolved/normalized), so callers can match on the
119
+ * source form.
120
+ */
121
+ skip?: (href: string) => boolean;
122
+ /**
123
+ * Treat explicit redirect sources as valid link targets. Defaults
124
+ * to `true` — unions literal `from` paths from `public/_redirects`
125
+ * (Netlify / Cloudflare Pages format) and literal keys from
126
+ * Astro's `redirects` config into the built-paths set, so linking
127
+ * to an intentional redirect doesn't warn.
128
+ *
129
+ * Set to `false` to treat only actual built pages as valid. Useful
130
+ * when you want to surface "this link hits a redirect, even if
131
+ * that's on purpose" — e.g. during an audit pass where you're
132
+ * actively eliminating redirect hops for performance.
133
+ */
134
+ honorRedirects?: boolean;
135
+ }
136
+ /**
137
+ * Configurable bounds for `validateMetadataLength`. Missing fields fall
138
+ * back to the defaults (title 30–65, description 70–200).
139
+ */
140
+ export interface MetadataLengthBounds {
141
+ title?: {
142
+ min?: number;
143
+ max?: number;
144
+ };
145
+ description?: {
146
+ min?: number;
147
+ max?: number;
148
+ };
149
+ }
150
+ /** Defaults applied when `validateMetadataLength: true`. */
151
+ export declare const DEFAULT_METADATA_LENGTH_BOUNDS: {
152
+ readonly title: {
153
+ readonly min: 30;
154
+ readonly max: 65;
155
+ };
156
+ readonly description: {
157
+ readonly min: 70;
158
+ readonly max: 200;
159
+ };
160
+ };
161
+ /**
162
+ * Resolve the `validateMetadataLength` option into concrete bounds, or
163
+ * `null` when the check is disabled. Exported so callers can reuse the
164
+ * same resolution in their own pipelines (and so tests can cover the
165
+ * merge logic without running a full build).
166
+ */
167
+ export declare function resolveMetadataLengthBounds(option: boolean | MetadataLengthBounds | undefined): {
168
+ title: {
169
+ min: number;
170
+ max: number;
171
+ };
172
+ description: {
173
+ min: number;
174
+ max: number;
175
+ };
176
+ } | null;
74
177
  export interface SeoGraphIntegrationOptions {
75
178
  /**
76
179
  * Warn when a built page has zero or more than one `<h1>` element.
@@ -88,6 +191,44 @@ export interface SeoGraphIntegrationOptions {
88
191
  * are present on multiple pages.
89
192
  */
90
193
  validateUniqueMetadata?: boolean;
194
+ /**
195
+ * Warn when a built page contains `<img>` tags without an `alt`
196
+ * attribute. Missing alt text is both an accessibility and SEO
197
+ * issue. Defaults to `true`.
198
+ *
199
+ * Decorative images with `alt=""` are treated as correct — that's
200
+ * the intended WCAG pattern for images that convey no information.
201
+ * Only a fully missing `alt` attribute triggers a warning.
202
+ */
203
+ validateImageAlt?: boolean;
204
+ /**
205
+ * Warn when `<title>` or `<meta name="description">` length falls
206
+ * outside SERP-friendly bounds. Defaults to `true` (title 30–65
207
+ * chars, description 70–200 chars).
208
+ *
209
+ * Pass `false` to disable, or an object to override the bounds.
210
+ * Length is measured on the whitespace-collapsed, entity-decoded
211
+ * text — the same thing Google displays in the SERP.
212
+ */
213
+ validateMetadataLength?: boolean | MetadataLengthBounds;
214
+ /**
215
+ * Warn when an internal `<a href>` points to a URL that doesn't
216
+ * match a built page. Catches two common classes of bug:
217
+ *
218
+ * - **Trailing-slash mismatches.** Linking to `/about-me` when the
219
+ * site emits `/about-me/` (or vice versa). The link "works" via
220
+ * a redirect but wastes a round-trip and hurts SEO.
221
+ * - **Broken internal links.** A link to a path that isn't in the
222
+ * build at all — a 404 waiting to happen.
223
+ *
224
+ * Defaults to `true`. Only checks links with the same origin as
225
+ * `config.site` (plus root-relative `/foo`-style links). External
226
+ * links, `mailto:`, `tel:`, fragment-only (`#x`), and SSR-only
227
+ * routes are not checked. Pass `false` to disable, or
228
+ * `{ skip: (href) => boolean }` to exclude specific hrefs (e.g.
229
+ * dynamic redirects handled at the host layer).
230
+ */
231
+ validateInternalLinks?: boolean | ValidateInternalLinksOptions;
91
232
  /**
92
233
  * Submit built URLs to IndexNow after the build completes. Omit to
93
234
  * disable. Only URLs on `host` are submitted; URLs with trailing
@@ -113,6 +254,79 @@ export declare function htmlFileToUrl(relativePath: string, siteUrl: string): st
113
254
  * a lint warning.
114
255
  */
115
256
  export declare function countH1s(html: string): number;
257
+ /**
258
+ * Parse a Netlify/Cloudflare Pages `_redirects` file and return the
259
+ * `from` paths of rules with literal sources (no wildcards or
260
+ * placeholders). The literal subset is enough to treat those paths as
261
+ * valid link targets — if a rule uses `*` or `:splat` it's dynamic and
262
+ * we can't meaningfully pre-validate hrefs against it.
263
+ *
264
+ * Format per line: `from to [status]`. `#` starts a comment. Blank
265
+ * lines are ignored. See:
266
+ * https://docs.netlify.com/routing/redirects/
267
+ * https://developers.cloudflare.com/pages/configuration/redirects/
268
+ */
269
+ export declare function parseNetlifyRedirects(content: string): string[];
270
+ /**
271
+ * Return the literal `from` paths from an Astro `config.redirects`
272
+ * object. Parameterized routes (`/old/[slug]` or `/old/*`) are skipped
273
+ * for the same reason `parseNetlifyRedirects` skips dynamic rules.
274
+ */
275
+ export declare function collectAstroRedirectSources(redirects: ConfigSetupHook['config']['redirects'] | undefined): string[];
276
+ /**
277
+ * Turn a built HTML file path into its canonical pathname (the `url`
278
+ * form without origin). Used by `validateInternalLinks` to build the
279
+ * set of paths that actually exist in the build.
280
+ */
281
+ export declare function htmlFileToPath(relativePath: string): string;
282
+ /**
283
+ * Extract all `<a href="...">` values from an HTML string. Returns the
284
+ * raw attribute values (not decoded) in document order.
285
+ */
286
+ export declare function extractAnchorHrefs(html: string): string[];
287
+ /**
288
+ * Classify an internal link's href against the set of built pathnames.
289
+ *
290
+ * Returns:
291
+ * - `{ status: 'external' }` for off-origin, `mailto:`, `tel:`,
292
+ * fragment-only, or non-http(s) schemes.
293
+ * - `{ status: 'ok' }` when the resolved pathname exactly matches a
294
+ * built page.
295
+ * - `{ status: 'trailing-slash', suggested }` when the other form
296
+ * (with/without trailing slash) matches — your case.
297
+ * - `{ status: 'not-found' }` when no form matches — a broken link.
298
+ *
299
+ * `origin` is the site's canonical origin (from Astro's `config.site`).
300
+ * When omitted, only root-relative hrefs (`/foo`) are classified;
301
+ * absolute URLs return `external`.
302
+ */
303
+ export declare function classifyInternalLink(href: string, builtPaths: ReadonlySet<string>, origin: string | undefined): {
304
+ status: 'external';
305
+ } | {
306
+ status: 'ok';
307
+ } | {
308
+ status: 'trailing-slash';
309
+ suggested: string;
310
+ } | {
311
+ status: 'not-found';
312
+ };
313
+ /**
314
+ * Find `<img>` tags missing an `alt` attribute. Returns the `src` value
315
+ * of each offending tag (or `"(no src)"` when src is absent too) so the
316
+ * warning can identify which image to fix.
317
+ *
318
+ * Two WCAG-sanctioned ways to mark an image as decorative are respected
319
+ * and NOT flagged:
320
+ *
321
+ * - `alt=""` — the canonical decorative-image pattern.
322
+ * - `role="presentation"` (or `role="none"`) — removes the image from
323
+ * the accessibility tree; typically paired with `alt=""` but some
324
+ * codebases use it alone.
325
+ *
326
+ * Only a tag that has neither an `alt` attribute nor a decorative role
327
+ * triggers a warning.
328
+ */
329
+ export declare function findImagesWithoutAlt(html: string): string[];
116
330
  /**
117
331
  * Extract the first `<title>` element's text content. Returns `null` when
118
332
  * no title tag is found. Whitespace-collapsed and entity-decoded so
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAKnE,UAAU,aAAa;IACnB,GAAG,EAAE,GAAG,CAAC;IAET,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC5F;AAED,UAAU,oBAAoB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtE,CAAC;CACL;AAED,MAAM,WAAW,0BAA0B;IACvC,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACtC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACvC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,0BAA0B,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAc3E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAgBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlE;AAgBD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,OAAO,GAAE,0BAA+B,GAAG,oBAAoB,CA4J/F"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,52 @@ import { join, relative } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { submitToIndexNow } from '@jdevalk/seo-graph-core';
5
5
  import { renderLlmsTxt } from './llms-txt.js';
6
+ /**
7
+ * Pathnames that `indexNow` excludes by default, regardless of any
8
+ * caller-supplied `filter`. The 404 page is the only universal case —
9
+ * search engines don't need to be notified about it, and submitting it
10
+ * wastes daily IndexNow quota.
11
+ *
12
+ * Matches both `/404` (from an Astro `src/pages/404.astro` → `404.html`
13
+ * that `htmlFileToUrl` strips to `/404`) and `/404/` (the directory form
14
+ * when the build emits `404/index.html`).
15
+ */
16
+ export function isDefaultExcludedFromIndexNow(url) {
17
+ let pathname;
18
+ try {
19
+ pathname = new URL(url).pathname;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ return pathname === '/404' || pathname === '/404/';
25
+ }
26
+ /** Defaults applied when `validateMetadataLength: true`. */
27
+ export const DEFAULT_METADATA_LENGTH_BOUNDS = {
28
+ title: { min: 30, max: 65 },
29
+ description: { min: 70, max: 200 },
30
+ };
31
+ /**
32
+ * Resolve the `validateMetadataLength` option into concrete bounds, or
33
+ * `null` when the check is disabled. Exported so callers can reuse the
34
+ * same resolution in their own pipelines (and so tests can cover the
35
+ * merge logic without running a full build).
36
+ */
37
+ export function resolveMetadataLengthBounds(option) {
38
+ if (option === false || option === undefined)
39
+ return null;
40
+ const overrides = option === true ? {} : option;
41
+ return {
42
+ title: {
43
+ min: overrides.title?.min ?? DEFAULT_METADATA_LENGTH_BOUNDS.title.min,
44
+ max: overrides.title?.max ?? DEFAULT_METADATA_LENGTH_BOUNDS.title.max,
45
+ },
46
+ description: {
47
+ min: overrides.description?.min ?? DEFAULT_METADATA_LENGTH_BOUNDS.description.min,
48
+ max: overrides.description?.max ?? DEFAULT_METADATA_LENGTH_BOUNDS.description.max,
49
+ },
50
+ };
51
+ }
6
52
  /**
7
53
  * Turn a built HTML file path (relative to the outDir, e.g.
8
54
  * `blog/post/index.html`) into an absolute URL on `siteUrl`. Rewrites
@@ -36,6 +82,177 @@ export function countH1s(html) {
36
82
  const matches = html.match(/<h1[\s>]/gi);
37
83
  return matches ? matches.length : 0;
38
84
  }
85
+ /**
86
+ * Parse a Netlify/Cloudflare Pages `_redirects` file and return the
87
+ * `from` paths of rules with literal sources (no wildcards or
88
+ * placeholders). The literal subset is enough to treat those paths as
89
+ * valid link targets — if a rule uses `*` or `:splat` it's dynamic and
90
+ * we can't meaningfully pre-validate hrefs against it.
91
+ *
92
+ * Format per line: `from to [status]`. `#` starts a comment. Blank
93
+ * lines are ignored. See:
94
+ * https://docs.netlify.com/routing/redirects/
95
+ * https://developers.cloudflare.com/pages/configuration/redirects/
96
+ */
97
+ export function parseNetlifyRedirects(content) {
98
+ const sources = [];
99
+ for (const rawLine of content.split(/\r?\n/)) {
100
+ const line = rawLine.trim();
101
+ if (line === '' || line.startsWith('#'))
102
+ continue;
103
+ const from = line.split(/\s+/)[0];
104
+ if (!from)
105
+ continue;
106
+ // Skip dynamic rules — we only union literal paths into the
107
+ // built-paths set. Glob/splat matching is out of scope.
108
+ if (/[*:]/.test(from))
109
+ continue;
110
+ // Ignore non-path sources (full URLs in _redirects denote
111
+ // proxy/rewrite rules; not a link target we'd validate).
112
+ if (!from.startsWith('/'))
113
+ continue;
114
+ sources.push(from);
115
+ }
116
+ return sources;
117
+ }
118
+ /**
119
+ * Return the literal `from` paths from an Astro `config.redirects`
120
+ * object. Parameterized routes (`/old/[slug]` or `/old/*`) are skipped
121
+ * for the same reason `parseNetlifyRedirects` skips dynamic rules.
122
+ */
123
+ export function collectAstroRedirectSources(redirects) {
124
+ if (!redirects)
125
+ return [];
126
+ const sources = [];
127
+ for (const from of Object.keys(redirects)) {
128
+ if (!from.startsWith('/'))
129
+ continue;
130
+ if (/[[\]*]/.test(from))
131
+ continue;
132
+ sources.push(from);
133
+ }
134
+ return sources;
135
+ }
136
+ /**
137
+ * Turn a built HTML file path into its canonical pathname (the `url`
138
+ * form without origin). Used by `validateInternalLinks` to build the
139
+ * set of paths that actually exist in the build.
140
+ */
141
+ export function htmlFileToPath(relativePath) {
142
+ const normalized = relativePath.split(/[\\/]/).join('/');
143
+ if (normalized === 'index.html' || normalized === '/index.html')
144
+ return '/';
145
+ if (normalized.endsWith('/index.html')) {
146
+ return '/' + normalized.slice(0, -'index.html'.length);
147
+ }
148
+ if (normalized.endsWith('.html')) {
149
+ return '/' + normalized.slice(0, -'.html'.length);
150
+ }
151
+ return '/' + normalized;
152
+ }
153
+ /**
154
+ * Extract all `<a href="...">` values from an HTML string. Returns the
155
+ * raw attribute values (not decoded) in document order.
156
+ */
157
+ export function extractAnchorHrefs(html) {
158
+ const hrefs = [];
159
+ for (const match of html.matchAll(/<a\b[^>]*\bhref\s*=\s*["']([^"']*)["'][^>]*>/gi)) {
160
+ hrefs.push(match[1]);
161
+ }
162
+ return hrefs;
163
+ }
164
+ /**
165
+ * Classify an internal link's href against the set of built pathnames.
166
+ *
167
+ * Returns:
168
+ * - `{ status: 'external' }` for off-origin, `mailto:`, `tel:`,
169
+ * fragment-only, or non-http(s) schemes.
170
+ * - `{ status: 'ok' }` when the resolved pathname exactly matches a
171
+ * built page.
172
+ * - `{ status: 'trailing-slash', suggested }` when the other form
173
+ * (with/without trailing slash) matches — your case.
174
+ * - `{ status: 'not-found' }` when no form matches — a broken link.
175
+ *
176
+ * `origin` is the site's canonical origin (from Astro's `config.site`).
177
+ * When omitted, only root-relative hrefs (`/foo`) are classified;
178
+ * absolute URLs return `external`.
179
+ */
180
+ export function classifyInternalLink(href, builtPaths, origin) {
181
+ if (!href)
182
+ return { status: 'external' };
183
+ // Same-page anchors and non-http schemes.
184
+ if (href.startsWith('#'))
185
+ return { status: 'external' };
186
+ if (/^(?:mailto:|tel:|javascript:|data:|blob:)/i.test(href))
187
+ return { status: 'external' };
188
+ let pathname;
189
+ if (href.startsWith('/') && !href.startsWith('//')) {
190
+ // Root-relative. Strip query + fragment.
191
+ pathname = href.split('#')[0].split('?')[0];
192
+ }
193
+ else if (/^https?:\/\//i.test(href) || href.startsWith('//')) {
194
+ // Absolute (or protocol-relative). Compare origins if we know ours.
195
+ if (!origin)
196
+ return { status: 'external' };
197
+ let parsed;
198
+ try {
199
+ parsed = new URL(href.startsWith('//') ? `https:${href}` : href);
200
+ }
201
+ catch {
202
+ return { status: 'external' };
203
+ }
204
+ let ourOrigin;
205
+ try {
206
+ ourOrigin = new URL(origin);
207
+ }
208
+ catch {
209
+ return { status: 'external' };
210
+ }
211
+ if (parsed.origin !== ourOrigin.origin)
212
+ return { status: 'external' };
213
+ pathname = parsed.pathname;
214
+ }
215
+ else {
216
+ // Relative or unknown shape — not worth guessing.
217
+ return { status: 'external' };
218
+ }
219
+ if (builtPaths.has(pathname))
220
+ return { status: 'ok' };
221
+ const flipped = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname + '/';
222
+ if (builtPaths.has(flipped)) {
223
+ return { status: 'trailing-slash', suggested: flipped };
224
+ }
225
+ return { status: 'not-found' };
226
+ }
227
+ /**
228
+ * Find `<img>` tags missing an `alt` attribute. Returns the `src` value
229
+ * of each offending tag (or `"(no src)"` when src is absent too) so the
230
+ * warning can identify which image to fix.
231
+ *
232
+ * Two WCAG-sanctioned ways to mark an image as decorative are respected
233
+ * and NOT flagged:
234
+ *
235
+ * - `alt=""` — the canonical decorative-image pattern.
236
+ * - `role="presentation"` (or `role="none"`) — removes the image from
237
+ * the accessibility tree; typically paired with `alt=""` but some
238
+ * codebases use it alone.
239
+ *
240
+ * Only a tag that has neither an `alt` attribute nor a decorative role
241
+ * triggers a warning.
242
+ */
243
+ export function findImagesWithoutAlt(html) {
244
+ const results = [];
245
+ for (const match of html.matchAll(/<img\b[^>]*>/gi)) {
246
+ const tag = match[0];
247
+ if (/\balt\s*=/i.test(tag))
248
+ continue;
249
+ if (/\brole\s*=\s*["'](?:presentation|none)["']/i.test(tag))
250
+ continue;
251
+ const src = tag.match(/\bsrc\s*=\s*["']([^"']*)["']/i)?.[1] ?? '(no src)';
252
+ results.push(src);
253
+ }
254
+ return results;
255
+ }
39
256
  const HTML_ENTITIES = {
40
257
  '&amp;': '&',
41
258
  '&lt;': '<',
@@ -106,20 +323,73 @@ async function collectHtmlFiles(dir, base = dir) {
106
323
  * ```
107
324
  */
108
325
  export default function seoGraph(options = {}) {
109
- const { validateH1 = true, validateUniqueMetadata = true, indexNow, llmsTxt } = options;
326
+ const { validateH1 = true, validateUniqueMetadata = true, validateImageAlt = true, validateMetadataLength = true, validateInternalLinks = true, indexNow, llmsTxt, } = options;
110
327
  const autoLlmsTxt = llmsTxt && !llmsTxt.sections;
328
+ const lengthBounds = resolveMetadataLengthBounds(validateMetadataLength);
329
+ const internalLinksEnabled = validateInternalLinks !== false;
330
+ const internalLinksSkip = typeof validateInternalLinks === 'object' ? validateInternalLinks.skip : undefined;
331
+ const internalLinksHonorRedirects = typeof validateInternalLinks === 'object'
332
+ ? (validateInternalLinks.honorRedirects ?? true)
333
+ : true;
334
+ // Captured in astro:config:setup so astro:build:done can classify
335
+ // absolute same-origin links against the site's canonical origin
336
+ // and treat Astro-configured redirect sources as valid link targets.
337
+ let siteOrigin;
338
+ let astroRedirectSources = [];
111
339
  return {
112
340
  name: '@jdevalk/astro-seo-graph',
113
341
  hooks: {
342
+ 'astro:config:setup': ({ config }) => {
343
+ if (config.site) {
344
+ try {
345
+ siteOrigin = new URL(config.site.toString()).origin;
346
+ }
347
+ catch {
348
+ // Leave undefined — validateInternalLinks will
349
+ // still work for root-relative hrefs.
350
+ }
351
+ }
352
+ astroRedirectSources = collectAstroRedirectSources(config.redirects);
353
+ },
114
354
  'astro:build:done': async ({ dir, logger }) => {
115
355
  const buildDir = fileURLToPath(dir);
116
- const needsContentScan = validateH1 || validateUniqueMetadata || autoLlmsTxt;
356
+ const needsContentScan = validateH1 ||
357
+ validateUniqueMetadata ||
358
+ validateImageAlt ||
359
+ lengthBounds !== null ||
360
+ internalLinksEnabled ||
361
+ autoLlmsTxt;
117
362
  const htmlFiles = needsContentScan || indexNow || llmsTxt ? await collectHtmlFiles(buildDir) : [];
363
+ let builtPaths = null;
364
+ if (internalLinksEnabled) {
365
+ builtPaths = new Set(htmlFiles.map(htmlFileToPath));
366
+ if (internalLinksHonorRedirects) {
367
+ // Treat explicit redirect sources as valid link
368
+ // targets. Reads `_redirects` (Netlify/Cloudflare
369
+ // Pages) from the build output and merges Astro's
370
+ // `config.redirects` captured at setup time.
371
+ try {
372
+ const redirectsContent = await readFile(join(buildDir, '_redirects'), 'utf8');
373
+ for (const from of parseNetlifyRedirects(redirectsContent)) {
374
+ builtPaths.add(from);
375
+ }
376
+ }
377
+ catch {
378
+ // No `_redirects` file; that's fine.
379
+ }
380
+ for (const from of astroRedirectSources) {
381
+ builtPaths.add(from);
382
+ }
383
+ }
384
+ }
118
385
  const autoLinks = [];
119
386
  const h1Missing = [];
120
387
  const h1Multiple = [];
121
388
  const titlesByValue = new Map();
122
389
  const descriptionsByValue = new Map();
390
+ const imagesMissingAlt = [];
391
+ const internalLinkIssues = [];
392
+ const lengthIssues = [];
123
393
  if (needsContentScan) {
124
394
  for (const file of htmlFiles) {
125
395
  const content = await readFile(join(buildDir, file), 'utf8');
@@ -130,8 +400,37 @@ export default function seoGraph(options = {}) {
130
400
  else if (count > 1)
131
401
  h1Multiple.push({ file, count });
132
402
  }
133
- const title = validateUniqueMetadata || autoLlmsTxt ? extractTitle(content) : null;
134
- const description = validateUniqueMetadata || autoLlmsTxt
403
+ if (validateImageAlt) {
404
+ const srcs = findImagesWithoutAlt(content);
405
+ if (srcs.length > 0)
406
+ imagesMissingAlt.push({ file, srcs });
407
+ }
408
+ if (builtPaths !== null) {
409
+ for (const href of extractAnchorHrefs(content)) {
410
+ if (internalLinksSkip && internalLinksSkip(href))
411
+ continue;
412
+ const result = classifyInternalLink(href, builtPaths, siteOrigin);
413
+ if (result.status === 'trailing-slash') {
414
+ internalLinkIssues.push({
415
+ file,
416
+ href,
417
+ kind: 'trailing-slash',
418
+ suggested: result.suggested,
419
+ });
420
+ }
421
+ else if (result.status === 'not-found') {
422
+ internalLinkIssues.push({
423
+ file,
424
+ href,
425
+ kind: 'not-found',
426
+ });
427
+ }
428
+ }
429
+ }
430
+ const needsTitle = validateUniqueMetadata || autoLlmsTxt || lengthBounds !== null;
431
+ const needsDescription = needsTitle;
432
+ const title = needsTitle ? extractTitle(content) : null;
433
+ const description = needsDescription
135
434
  ? extractMetaDescription(content)
136
435
  : null;
137
436
  if (validateUniqueMetadata) {
@@ -146,6 +445,52 @@ export default function seoGraph(options = {}) {
146
445
  descriptionsByValue.set(description, list);
147
446
  }
148
447
  }
448
+ if (lengthBounds !== null) {
449
+ if (title) {
450
+ if (title.length < lengthBounds.title.min) {
451
+ lengthIssues.push({
452
+ file,
453
+ field: 'title',
454
+ length: title.length,
455
+ bound: 'short',
456
+ limit: lengthBounds.title.min,
457
+ value: title,
458
+ });
459
+ }
460
+ else if (title.length > lengthBounds.title.max) {
461
+ lengthIssues.push({
462
+ file,
463
+ field: 'title',
464
+ length: title.length,
465
+ bound: 'long',
466
+ limit: lengthBounds.title.max,
467
+ value: title,
468
+ });
469
+ }
470
+ }
471
+ if (description) {
472
+ if (description.length < lengthBounds.description.min) {
473
+ lengthIssues.push({
474
+ file,
475
+ field: 'description',
476
+ length: description.length,
477
+ bound: 'short',
478
+ limit: lengthBounds.description.min,
479
+ value: description,
480
+ });
481
+ }
482
+ else if (description.length > lengthBounds.description.max) {
483
+ lengthIssues.push({
484
+ file,
485
+ field: 'description',
486
+ length: description.length,
487
+ bound: 'long',
488
+ limit: lengthBounds.description.max,
489
+ value: description,
490
+ });
491
+ }
492
+ }
493
+ }
149
494
  if (autoLlmsTxt && llmsTxt) {
150
495
  const url = htmlFileToUrl(file, llmsTxt.siteUrl);
151
496
  if (!llmsTxt.filter || llmsTxt.filter(url)) {
@@ -171,6 +516,47 @@ export default function seoGraph(options = {}) {
171
516
  }
172
517
  }
173
518
  }
519
+ if (builtPaths !== null) {
520
+ if (internalLinkIssues.length === 0) {
521
+ logger.info(`Internal links: ${htmlFiles.length} pages checked, all good.`);
522
+ }
523
+ else {
524
+ for (const issue of internalLinkIssues) {
525
+ if (issue.kind === 'trailing-slash') {
526
+ logger.warn(`Internal links: ${issue.file} links to ${JSON.stringify(issue.href)} but built page is ${JSON.stringify(issue.suggested)} (redirect on every visit).`);
527
+ }
528
+ else {
529
+ logger.warn(`Internal links: ${issue.file} links to ${JSON.stringify(issue.href)} which isn't in the build (404).`);
530
+ }
531
+ }
532
+ }
533
+ }
534
+ if (validateImageAlt) {
535
+ if (imagesMissingAlt.length === 0) {
536
+ logger.info(`Image alt validation: ${htmlFiles.length} pages checked, all good.`);
537
+ }
538
+ else {
539
+ for (const { file, srcs } of imagesMissingAlt) {
540
+ const preview = srcs.slice(0, 5).join(', ');
541
+ const more = srcs.length > 5 ? ` (+${srcs.length - 5} more)` : '';
542
+ logger.warn(`Image alt validation: ${file} has ${srcs.length} <img> without alt: ${preview}${more}`);
543
+ }
544
+ }
545
+ }
546
+ if (lengthBounds !== null) {
547
+ if (lengthIssues.length === 0) {
548
+ logger.info(`Metadata length: ${htmlFiles.length} pages checked, all good.`);
549
+ }
550
+ else {
551
+ for (const issue of lengthIssues) {
552
+ const direction = issue.bound === 'short' ? 'min' : 'max';
553
+ const preview = issue.value.length > 80
554
+ ? issue.value.slice(0, 77) + '…'
555
+ : issue.value;
556
+ logger.warn(`Metadata length: ${issue.file} ${issue.field} is ${issue.length} chars (${direction} ${issue.limit}): ${JSON.stringify(preview)}`);
557
+ }
558
+ }
559
+ }
174
560
  if (validateUniqueMetadata) {
175
561
  const dupTitles = [...titlesByValue.entries()].filter(([, files]) => files.length > 1);
176
562
  const dupDescriptions = [...descriptionsByValue.entries()].filter(([, files]) => files.length > 1);
@@ -192,6 +578,7 @@ export default function seoGraph(options = {}) {
192
578
  if (indexNow) {
193
579
  const urls = htmlFiles
194
580
  .map((f) => htmlFileToUrl(f, indexNow.siteUrl))
581
+ .filter((u) => !isDefaultExcludedFromIndexNow(u))
195
582
  .filter((u) => (indexNow.filter ? indexNow.filter(u) : true));
196
583
  if (urls.length === 0) {
197
584
  logger.info('IndexNow: no URLs to submit.');
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdevalk/astro-seo-graph",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Astro integration for @jdevalk/seo-graph-core. Seo component, route factories, content-collection aggregator, Zod content helpers.",
5
5
  "keywords": [
6
6
  "astro",