@conduction/docusaurus-preset 3.6.2 → 3.7.1
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/bin/validate-ai-baseline.mjs +18 -12
- package/package.json +1 -1
- package/src/index.js +90 -32
|
@@ -87,25 +87,31 @@ check('sitemap.xml exists and has at least 1 URL', () => {
|
|
|
87
87
|
return {ok: true, msg: `${n} URLs`};
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
/* sitemap.xml lastmod
|
|
91
|
-
lastmod as the only sitemap-level signal that actually informs
|
|
92
|
-
recrawl priority,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
/* sitemap.xml should ship <lastmod> on the majority of URLs. Google
|
|
91
|
+
treats lastmod as the only sitemap-level signal that actually informs
|
|
92
|
+
recrawl priority, and only when it's trustworthy. Preset 3.7+ wraps
|
|
93
|
+
user-supplied opts.presets to inject DEFAULT_SITEMAP_OPTIONS
|
|
94
|
+
(lastmod: 'date') into any classic preset entry, so every site that
|
|
95
|
+
bumps should see lastmod automatically.
|
|
96
|
+
|
|
97
|
+
Hard-fail when lastmod is missing entirely (means the preset wrap
|
|
98
|
+
didn't kick in). Pass when at least half of URLs have lastmod —
|
|
99
|
+
Docusaurus' auto-generated routes (/docs/category/X/, the root path
|
|
100
|
+
without a source file, redirects, etc.) legitimately don't have a
|
|
101
|
+
git mtime to use, so 100% coverage is unrealistic. ~80% is typical
|
|
102
|
+
for a content-heavy docs site. */
|
|
103
|
+
check('sitemap.xml emits <lastmod> on URLs', () => {
|
|
100
104
|
const body = readBuild('sitemap.xml');
|
|
101
105
|
const locCount = (body.match(/<loc>/g) || []).length;
|
|
102
106
|
const lastmodCount = (body.match(/<lastmod>/g) || []).length;
|
|
103
107
|
if (locCount === 0) return {ok: false, msg: 'no <loc> entries to compare against'};
|
|
104
108
|
if (lastmodCount === 0) {
|
|
105
|
-
|
|
106
|
-
return {ok: true, msg: `WARN 0 / ${locCount} URLs have <lastmod> (enable sitemap.lastmod in docusaurus.config)`};
|
|
109
|
+
return {ok: false, msg: `0 / ${locCount} URLs have <lastmod>. Upgrade to @conduction/docusaurus-preset ^3.7.0 or set sitemap.lastmod in docusaurus.config.`};
|
|
107
110
|
}
|
|
108
111
|
const ratio = lastmodCount / locCount;
|
|
112
|
+
if (ratio < 0.5) {
|
|
113
|
+
return {ok: false, msg: `only ${lastmodCount} / ${locCount} URLs have <lastmod> (under 50%); investigate which routes are missing source files`};
|
|
114
|
+
}
|
|
109
115
|
return {ok: true, msg: `${lastmodCount} / ${locCount} URLs (${Math.round(ratio * 100)}%)`};
|
|
110
116
|
});
|
|
111
117
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -292,6 +292,41 @@ const I18N = {
|
|
|
292
292
|
},
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Wrap a user-supplied `opts.presets` array so the classic preset's
|
|
297
|
+
* `sitemap` option inherits brand defaults (lastmod, ignorePatterns)
|
|
298
|
+
* when the site hasn't set them explicitly. Before this helper, sites
|
|
299
|
+
* that passed their own presets array silently lost the preset's
|
|
300
|
+
* DEFAULT_SITEMAP_OPTIONS, so the fleet sitemaps never shipped
|
|
301
|
+
* <lastmod> tags despite the preset claiming to add them. See
|
|
302
|
+
* MEMORY.md project_preset-4.0-wrap-user-presets for the back story.
|
|
303
|
+
*
|
|
304
|
+
* Recognises both 'classic' and '@docusaurus/preset-classic' entries.
|
|
305
|
+
* Leaves non-classic presets untouched. Explicit user values win:
|
|
306
|
+
* `sitemap: null` opts out, `sitemap: { lastmod: false }` opts out
|
|
307
|
+
* of lastmod specifically, and so on.
|
|
308
|
+
*/
|
|
309
|
+
function wrapClassicPresetDefaults(userPresets) {
|
|
310
|
+
return userPresets.map(entry => {
|
|
311
|
+
if (!Array.isArray(entry)) return entry;
|
|
312
|
+
const [name, config] = entry;
|
|
313
|
+
const isClassic =
|
|
314
|
+
name === 'classic' || name === '@docusaurus/preset-classic';
|
|
315
|
+
if (!isClassic || typeof config !== 'object' || config === null) return entry;
|
|
316
|
+
if (config.sitemap === null) return entry; /* explicit opt-out */
|
|
317
|
+
return [
|
|
318
|
+
name,
|
|
319
|
+
{
|
|
320
|
+
...config,
|
|
321
|
+
sitemap: {
|
|
322
|
+
...DEFAULT_SITEMAP_OPTIONS,
|
|
323
|
+
...(config.sitemap || {}),
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
];
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
295
330
|
/**
|
|
296
331
|
* Brand-default navbar. Sites pass their own items[] and logo; the chrome
|
|
297
332
|
* styling (cobalt-on-white, Plex-Mono caption) is locked.
|
|
@@ -476,30 +511,34 @@ function createConfig(opts) {
|
|
|
476
511
|
|
|
477
512
|
i18n: opts.i18n || I18N,
|
|
478
513
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
514
|
+
/* Sites can pass `opts.presets` to override docs/blog/theme. When
|
|
515
|
+
they do, the preset wraps each classic preset entry to deep-merge
|
|
516
|
+
DEFAULT_SITEMAP_OPTIONS into the entry's sitemap key (lastmod
|
|
517
|
+
becomes automatic, ignorePatterns merge in). Without this wrap
|
|
518
|
+
sites would have to copy-paste the sitemap config in every
|
|
519
|
+
docusaurus.config.js, and the fleet would drift over time. */
|
|
520
|
+
presets: opts.presets
|
|
521
|
+
? wrapClassicPresetDefaults(opts.presets)
|
|
522
|
+
: [
|
|
523
|
+
[
|
|
524
|
+
'classic',
|
|
525
|
+
{
|
|
526
|
+
docs: {
|
|
527
|
+
sidebarPath: './sidebars.js',
|
|
528
|
+
editUrl: opts.editUrl,
|
|
529
|
+
},
|
|
530
|
+
blog: opts.blog === false ? false : {
|
|
531
|
+
showReadingTime: true,
|
|
532
|
+
blogTitle: opts.title + ' blog',
|
|
533
|
+
blogDescription: 'Updates from Conduction',
|
|
534
|
+
},
|
|
535
|
+
theme: {
|
|
536
|
+
customCss,
|
|
537
|
+
},
|
|
538
|
+
sitemap: DEFAULT_SITEMAP_OPTIONS,
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
],
|
|
503
542
|
|
|
504
543
|
/* Brand theme: registers ./theme/* swizzles (Navbar, Footer, …)
|
|
505
544
|
and auto-loads brand.css. Site-specific themes can be added by
|
|
@@ -581,15 +620,34 @@ function createConfig(opts) {
|
|
|
581
620
|
opts.legalLinks || {}
|
|
582
621
|
),
|
|
583
622
|
/* AI-friendly social-card defaults. `image` ships from the
|
|
584
|
-
preset's static/img/og-conduction.png and gets served at
|
|
585
|
-
consuming site's /img/og-conduction.png; drop your
|
|
586
|
-
static/img/og-conduction.png to override per-site
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
623
|
+
preset's static/img/og-conduction.png and gets served at
|
|
624
|
+
every consuming site's /img/og-conduction.png; drop your
|
|
625
|
+
own static/img/og-conduction.png to override per-site, or
|
|
626
|
+
pass `themeConfig.image: 'img/og-my-app.png'` to use a
|
|
627
|
+
different file. `metadata` seeds twitter:site + twitter:card
|
|
628
|
+
+ og:type baselines; per-page MDX frontmatter still wins
|
|
629
|
+
via Helmet de-dupe.
|
|
630
|
+
|
|
631
|
+
These two slots are handled below via explicit overrides
|
|
632
|
+
rather than the wholesale Object.assign so that user-set
|
|
633
|
+
metadata extends (rather than replaces) the brand defaults
|
|
634
|
+
and image falls back gracefully. */
|
|
635
|
+
image: opts.themeConfig?.image || DEFAULT_OG_IMAGE,
|
|
636
|
+
metadata: [
|
|
637
|
+
...DEFAULT_METADATA,
|
|
638
|
+
...(opts.themeConfig?.metadata || []),
|
|
639
|
+
],
|
|
591
640
|
},
|
|
592
|
-
opts.themeConfig
|
|
641
|
+
/* Object.assign last so opts.themeConfig overrides primitives
|
|
642
|
+
like colorMode and navbar, but the image + metadata keys
|
|
643
|
+
above pre-merged the user's values so the spread doesn't
|
|
644
|
+
clobber the brand defaults. */
|
|
645
|
+
(() => {
|
|
646
|
+
if (!opts.themeConfig) return {};
|
|
647
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
648
|
+
const {image: _image, metadata: _metadata, ...rest} = opts.themeConfig;
|
|
649
|
+
return rest;
|
|
650
|
+
})()
|
|
593
651
|
),
|
|
594
652
|
|
|
595
653
|
/* AI-crawler discovery: Organization + WebSite JSON-LD on every
|