dogsbay 0.2.0-beta.55 → 0.2.0-beta.57

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.
@@ -367,7 +367,16 @@ function buildDogsbayConfig(opts) {
367
367
  // them so format-astro skips emitting routes — the .md files
368
368
  // stay on disk so includes resolve, but they don't become
369
369
  // URLs. See plans/build-at-scale.md.
370
- ` excludeFromRoutes:`, ` - _attributes`, ` - modules`, ` - snippets`, ` - includes`);
370
+ ` excludeFromRoutes:`, ` - _attributes`, ` - modules`, ` - snippets`, ` - includes`,
371
+ // The AsciiBinder topic_map → nav.yml is the authoritative page
372
+ // list. routablesFrom: nav restricts the page set to pages the nav
373
+ // names; everything else (modules, fragments) is pulled into
374
+ // assemblies via `{% include %}` and never emitted standalone — so
375
+ // no fragment leaks into routes / llms.txt / search, and the build
376
+ // skips the ~85% of files that are includes (10.7k → ~1.7k on
377
+ // OpenShift). excludeFromRoutes above stays as the fallback for
378
+ // `routablesFrom: all`. See plans/nav-scoped-processing.md.
379
+ ` routablesFrom: nav`);
371
380
  // Persist the migration-time --attribute values so subsequent
372
381
  // `dogsbay site build` runs resolve {{ var }} / {% if %}
373
382
  // without re-passing flags. The build-time preprocessor
@@ -196,6 +196,14 @@ export async function siteBuild(cwd, options) {
196
196
  console.log(` Preprocessor: ${changes.join(", ")} → ${mirrorRoot}`);
197
197
  }
198
198
  }
199
+ // --no-astro: stop after the preprocessor. The resolved-Minja mirror under
200
+ // <siteRoot>/.dogsbay/build-content/ IS the artifact ("preprocessed
201
+ // markdown"); skip content import + Astro emission. Powers the Docker
202
+ // `preprocess` stage and serves as a debug aid.
203
+ if (options.astro === false) {
204
+ console.log(" --no-astro: stopped after preprocess (resolved MD in .dogsbay/build-content/)");
205
+ return;
206
+ }
199
207
  // 7. Import content
200
208
  //
201
209
  // Reads from `importPaths` — either the source paths (no-preprocess)
@@ -216,6 +224,23 @@ export async function siteBuild(cwd, options) {
216
224
  // local preview. Nav entries pointing at filtered pages are pruned
217
225
  // recursively so the sidebar doesn't have dead links.
218
226
  let { pages, nav, draftCount } = filterDrafts(importResult.pages, importResult.nav, options.includeDrafts === true, config.site.basePath);
227
+ // 7a. routablesFrom: "nav" — restrict the page set to pages the nav
228
+ // lists (the whitelist). Include-only fragments (modules/, _attributes/,
229
+ // etc.) are inlined into their assemblies by the preprocessor, so they
230
+ // never need to be standalone pages; dropping them here keeps them out
231
+ // of autodoc, ref-validation, llms.txt, the sitemap, and emit — no
232
+ // fragment-detection needed. Self-safe: a no-op when the nav is
233
+ // auto-derived (it already covers every page) or routablesFrom is
234
+ // "all". See plans/nav-scoped-processing.md.
235
+ if (config.content.routablesFrom === "nav") {
236
+ const before = pages.length;
237
+ const { kept, dropped } = filterPagesToNav(pages, nav, config.site.basePath);
238
+ if (dropped.length > 0) {
239
+ pages = kept;
240
+ console.log(` routablesFrom: nav — excluded ${dropped.length} non-nav page(s) of ${before}` +
241
+ ` (e.g. ${dropped.slice(0, 5).join(", ")})`);
242
+ }
243
+ }
219
244
  // 7b. onContentImported — let plugins mutate the page set + nav.
220
245
  if (resolvedPlugins.some((p) => p.plugin.onContentImported)) {
221
246
  const after = await runOnContentImported(resolvedPlugins, baseCtx, pages, nav);
@@ -669,3 +694,56 @@ function hrefMatchesDroppedSlug(href, droppedSlugs, basePath) {
669
694
  }
670
695
  return droppedSlugs.has(normalized) || droppedSlugs.has(normalized || "index");
671
696
  }
697
+ /**
698
+ * Collect the slug of every nav leaf (recursively), normalized the same
699
+ * way `hrefMatchesDroppedSlug` normalizes a single href — strip
700
+ * leading/trailing slashes, strip leading basePath segments; an empty
701
+ * result (the home href `/`) maps to `index`. Used by
702
+ * `content.routablesFrom: "nav"` to restrict the page set to
703
+ * nav-reachable pages.
704
+ */
705
+ function collectNavSlugs(items, basePath) {
706
+ const out = new Set();
707
+ const bp = normalizeBasePath(basePath);
708
+ const segs = basePathSegments(bp);
709
+ const walk = (nodes) => {
710
+ for (const item of nodes) {
711
+ if (item.href) {
712
+ let normalized = item.href.replace(/^\/+/, "").replace(/\/+$/, "");
713
+ for (const seg of segs) {
714
+ if (normalized === seg) {
715
+ normalized = "";
716
+ break;
717
+ }
718
+ if (normalized.startsWith(`${seg}/`)) {
719
+ normalized = normalized.slice(seg.length + 1);
720
+ }
721
+ }
722
+ out.add(normalized || "index");
723
+ }
724
+ if (item.children && item.children.length > 0)
725
+ walk(item.children);
726
+ }
727
+ };
728
+ walk(items);
729
+ return out;
730
+ }
731
+ /**
732
+ * Partition pages by nav membership for `content.routablesFrom: "nav"`.
733
+ * `kept` = pages whose slug is reachable from the nav; `dropped` = the
734
+ * slugs of pages the nav omits (include-only fragments — their content
735
+ * is already inlined into the assemblies that include them). Pure +
736
+ * exported for unit testing.
737
+ */
738
+ export function filterPagesToNav(pages, nav, basePath) {
739
+ const navSlugs = collectNavSlugs(nav, basePath);
740
+ const kept = [];
741
+ const dropped = [];
742
+ for (const page of pages) {
743
+ if (navSlugs.has(page.slug))
744
+ kept.push(page);
745
+ else
746
+ dropped.push(page.slug);
747
+ }
748
+ return { kept, dropped };
749
+ }
@@ -466,6 +466,13 @@ function validateContent(content, sourcePath) {
466
466
  }
467
467
  excludeFromRoutes = c.excludeFromRoutes;
468
468
  }
469
+ let routablesFrom;
470
+ if (c.routablesFrom !== undefined) {
471
+ if (c.routablesFrom !== "all" && c.routablesFrom !== "nav") {
472
+ throw new Error(`content.routablesFrom must be "all" or "nav" in ${sourcePath}`);
473
+ }
474
+ routablesFrom = c.routablesFrom;
475
+ }
469
476
  return {
470
477
  sources: validatedSources,
471
478
  section: c.section,
@@ -476,6 +483,7 @@ function validateContent(content, sourcePath) {
476
483
  locales,
477
484
  defaultLocale,
478
485
  excludeFromRoutes,
486
+ routablesFrom,
479
487
  };
480
488
  }
481
489
  function validateLocales(raw, sourcePath) {
package/dist/index.js CHANGED
@@ -130,6 +130,9 @@ site
130
130
  .option("--no-preprocess", "Skip the build-time Minja preprocessor pass even when " +
131
131
  "`attributes:` are declared. Debug aid — shows the raw " +
132
132
  "`{{ var }}` directives the importer left unresolved.")
133
+ .option("--no-astro", "Stop after the Minja preprocessor — write the resolved-Markdown " +
134
+ "mirror to .dogsbay/build-content/ but skip content import + Astro " +
135
+ "page emission. The mirror is the 'preprocessed markdown' artifact.")
133
136
  .action((dir, options) => siteBuild(dir, options));
134
137
  site
135
138
  .command("dev")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dogsbay",
3
- "version": "0.2.0-beta.55",
3
+ "version": "0.2.0-beta.57",
4
4
  "description": "CLI for Dogsbay — scaffold, build, and serve documentation sites with markdown / MkDocs / Obsidian / OpenAPI sources",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,18 +33,18 @@
33
33
  "picocolors": "^1.1.0",
34
34
  "prompts": "^2.4.2",
35
35
  "yaml": "^2.8.3",
36
- "@dogsbay/autodoc-python": "0.2.0-beta.55",
37
- "@dogsbay/format-obsidian": "0.2.0-beta.55",
38
- "@dogsbay/format-mkdocs": "0.2.0-beta.55",
39
- "@dogsbay/format-mdx": "0.2.0-beta.55",
40
- "@dogsbay/format-astro": "0.2.0-beta.55",
41
- "@dogsbay/format-starlight": "0.2.0-beta.55",
42
- "@dogsbay/format-dogsbay-md": "0.2.0-beta.55",
43
- "@dogsbay/format-openapi": "0.2.0-beta.55",
44
- "@dogsbay/adoc2md-modular": "0.2.0-beta.55",
45
- "@dogsbay/format-asciidoc": "0.2.0-beta.55",
46
- "@dogsbay/minja": "0.2.0-beta.55",
47
- "@dogsbay/types": "0.2.0-beta.55"
36
+ "@dogsbay/format-mkdocs": "0.2.0-beta.57",
37
+ "@dogsbay/format-astro": "0.2.0-beta.57",
38
+ "@dogsbay/autodoc-python": "0.2.0-beta.57",
39
+ "@dogsbay/format-obsidian": "0.2.0-beta.57",
40
+ "@dogsbay/format-mdx": "0.2.0-beta.57",
41
+ "@dogsbay/format-starlight": "0.2.0-beta.57",
42
+ "@dogsbay/format-dogsbay-md": "0.2.0-beta.57",
43
+ "@dogsbay/format-openapi": "0.2.0-beta.57",
44
+ "@dogsbay/adoc2md-modular": "0.2.0-beta.57",
45
+ "@dogsbay/format-asciidoc": "0.2.0-beta.57",
46
+ "@dogsbay/minja": "0.2.0-beta.57",
47
+ "@dogsbay/types": "0.2.0-beta.57"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/markdown-it": "^14.1.0",