dogsbay 0.2.0-beta.42 → 0.2.0-beta.43

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.
@@ -99,7 +99,15 @@ export async function migrateMkdocs(source, options) {
99
99
  // snippets, macros, autodoc, variants — see @dogsbay/format-
100
100
  // mkdocs's CLAUDE.md) runs identically to import-mkdocs; only
101
101
  // the final write target differs.
102
- const { pageCount, lossy } = await collectAndWriteContent(sourceDir, fullDocsDir, outputDir, config, { inlineAutodoc: options.inlineAutodoc ?? false });
102
+ // Lift the mkdocstrings Python handler options from mkdocs.yml.
103
+ // These become the project-level `autodoc.python` config block and
104
+ // also feed `--inline-autodoc`'s parser globalOptions — see
105
+ // plans/autodoc-mkdocstrings-options.md.
106
+ const autodocPythonOptions = extractAutodocOptions(config);
107
+ const { pageCount, lossy } = await collectAndWriteContent(sourceDir, fullDocsDir, outputDir, config, {
108
+ inlineAutodoc: options.inlineAutodoc ?? false,
109
+ autodocPythonOptions,
110
+ });
103
111
  console.log(pc.green(`Wrote`) + ` ${pageCount} pages to ./content/ as Dogsbay-MD`);
104
112
  // 3. Convert MkDocs nav: → ./content/nav.yml in canonical
105
113
  // single-key-map shape (- Label: file.md / nested children).
@@ -163,6 +171,15 @@ export async function migrateMkdocs(source, options) {
163
171
  llmsTxt: true,
164
172
  mdMirror: true,
165
173
  },
174
+ // Project-level autodoc render options lifted from the source
175
+ // mkdocs.yml's mkdocstrings handler. resolve-autodoc applies
176
+ // these under per-directive props so migrated reference pages
177
+ // render with the same signature/member-order behaviour the
178
+ // MkDocs site configured. Omitted when the source declared no
179
+ // renderer-relevant options.
180
+ ...(autodocPythonOptions
181
+ ? { autodoc: { python: autodocPythonOptions } }
182
+ : {}),
166
183
  };
167
184
  writeFileSync(join(outputDir, "dogsbay.config.yml"), serializeConfig(dogsbayConfig, "yaml"));
168
185
  console.log(pc.green(`Wrote`) + ` dogsbay.config.yml`);
@@ -304,8 +321,16 @@ async function collectAndWriteContent(sourceDir, fullDocsDir, outputDir, mkdocsC
304
321
  if (opts.inlineAutodoc && autodocSourceRootAbs) {
305
322
  // Snapshot mode: resolve at migration time. The resolved api-*
306
323
  // TreeNodes serialize to Dogsbay-MD `:::api-*` directives via
307
- // format-dogsbay-md's Phase 4a cases.
308
- parseOpts.autodoc = { sourceRoot: autodocSourceRootAbs };
324
+ // format-dogsbay-md's Phase 4a cases. globalOptions carries the
325
+ // mkdocstrings handler options so the snapshot matches what the
326
+ // MkDocs site configured (members_order, merge_init_into_class,
327
+ // …) rather than the bare mkdocstrings defaults.
328
+ parseOpts.autodoc = {
329
+ sourceRoot: autodocSourceRootAbs,
330
+ ...(opts.autodocPythonOptions
331
+ ? { globalOptions: opts.autodocPythonOptions }
332
+ : {}),
333
+ };
309
334
  }
310
335
  // Preserve mode (default): no `autodoc` in parseOpts. The `:::`
311
336
  // paragraphs stay as raw text; we walk them in post-process and
@@ -453,6 +478,60 @@ function extractMkdocstringsConfig(mkdocsConfig) {
453
478
  }
454
479
  return null;
455
480
  }
481
+ /**
482
+ * snake_case mkdocstrings handler option → camelCase Dogsbay
483
+ * `AutodocPythonOptions` key. Options the autodoc renderer doesn't
484
+ * understand (e.g. `extensions`, `preload_modules`,
485
+ * `docstring_section_style`) are intentionally absent — they fall
486
+ * through and are dropped. Mirrors the map in
487
+ * `format-mkdocs/src/importer.ts` so `migrate-mkdocs` and
488
+ * `import-mkdocs` honour the same handler options.
489
+ */
490
+ const MKDOCSTRINGS_OPTION_MAP = {
491
+ show_source: "showSource",
492
+ show_bases: "showBases",
493
+ show_signature: "showSignature",
494
+ unwrap_annotated: "unwrapAnnotated",
495
+ group_by_category: "groupByCategory",
496
+ show_root_full_path: "showRootFullPath",
497
+ show_root_heading: "showRootHeading",
498
+ show_if_no_docstring: "showIfNoDocstring",
499
+ inherited_members: "inheritedMembers",
500
+ merge_init_into_class: "mergeInitIntoClass",
501
+ separate_signature: "separateSignature",
502
+ members_order: "membersOrder",
503
+ filters: "filters",
504
+ heading_level: "headingLevel",
505
+ };
506
+ /**
507
+ * Lift the mkdocstrings Python handler `options:` block out of
508
+ * mkdocs.yml and map it to `AutodocPythonOptions`.
509
+ *
510
+ * A MkDocs site configures the handler once; `import-mkdocs` threads
511
+ * those options into the autodoc renderer as `globalOptions`. Without
512
+ * this, migrated `:::autodoc` directives fall back to the bare
513
+ * mkdocstrings defaults (`merge_init_into_class: false`,
514
+ * `members_order: alphabetical`) — the reference pages lose the
515
+ * expanded constructor signature and the document-order member list.
516
+ *
517
+ * Returns null when mkdocstrings isn't declared or sets no
518
+ * renderer-relevant options. See plans/autodoc-mkdocstrings-options.md.
519
+ */
520
+ function extractAutodocOptions(mkdocsConfig) {
521
+ const mkdocstrings = extractMkdocstringsConfig(mkdocsConfig);
522
+ if (!mkdocstrings)
523
+ return null;
524
+ const raw = mkdocstrings.handlers?.python?.options;
525
+ if (!raw || typeof raw !== "object")
526
+ return null;
527
+ const src = raw;
528
+ const out = {};
529
+ for (const [snake, camel] of Object.entries(MKDOCSTRINGS_OPTION_MAP)) {
530
+ if (src[snake] !== undefined)
531
+ out[camel] = src[snake];
532
+ }
533
+ return Object.keys(out).length > 0 ? out : null;
534
+ }
456
535
  /**
457
536
  * Extract macros plugin data file mappings from mkdocs.yml.
458
537
  * Returns { varName: absolutePath } for each `include_yaml` entry,
@@ -176,6 +176,10 @@ export async function siteBuild(cwd, options) {
176
176
  const { resolveAutodocRefs } = await import("../resolve-autodoc.js");
177
177
  const autodocResult = await resolveAutodocRefs(pages, {
178
178
  configDir: dirname(configPath),
179
+ // Project-level autodoc render options (lifted from the source
180
+ // mkdocs.yml by migrate-mkdocs). Layered under per-directive
181
+ // props — see plans/autodoc-mkdocstrings-options.md.
182
+ pythonOptions: config.autodoc?.python,
179
183
  });
180
184
  if (autodocResult.resolved > 0 || autodocResult.failed > 0) {
181
185
  const parts = [];
@@ -94,6 +94,7 @@ function validate(parsed, sourcePath) {
94
94
  throw new Error(`analytics must be an object in ${sourcePath}`);
95
95
  }
96
96
  const agent = validateAgent(obj.agent, sourcePath);
97
+ const autodoc = validateAutodoc(obj.autodoc, sourcePath);
97
98
  const taxonomies = validateTaxonomies(obj.taxonomies, sourcePath);
98
99
  const propSchema = validatePropSchema(obj.propSchema, sourcePath);
99
100
  const plugins = validatePlugins(obj.plugins, sourcePath);
@@ -108,11 +109,66 @@ function validate(parsed, sourcePath) {
108
109
  deploy: obj.deploy,
109
110
  analytics: obj.analytics,
110
111
  agent,
112
+ autodoc,
111
113
  taxonomies,
112
114
  propSchema,
113
115
  plugins,
114
116
  };
115
117
  }
118
+ /**
119
+ * Validate the optional `autodoc:` block. One sub-block per language
120
+ * handler — only `python` exists today. Field names mirror
121
+ * `AutodocPythonOptions`; type-checked individually so a typo'd
122
+ * boolean doesn't silently flip a render default.
123
+ */
124
+ function validateAutodoc(raw, sourcePath) {
125
+ if (raw === undefined)
126
+ return undefined;
127
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
128
+ throw new Error(`autodoc must be an object in ${sourcePath}`);
129
+ }
130
+ const a = raw;
131
+ if (a.python === undefined)
132
+ return {};
133
+ if (typeof a.python !== "object" || a.python === null || Array.isArray(a.python)) {
134
+ throw new Error(`autodoc.python must be an object in ${sourcePath}`);
135
+ }
136
+ const p = a.python;
137
+ const booleanKeys = [
138
+ "mergeInitIntoClass",
139
+ "separateSignature",
140
+ "unwrapAnnotated",
141
+ "inheritedMembers",
142
+ "showIfNoDocstring",
143
+ "showRootHeading",
144
+ "showRootFullPath",
145
+ "showBases",
146
+ "showSource",
147
+ "showSignature",
148
+ "groupByCategory",
149
+ ];
150
+ for (const key of booleanKeys) {
151
+ if (p[key] !== undefined && typeof p[key] !== "boolean") {
152
+ throw new Error(`autodoc.python.${key} must be a boolean in ${sourcePath}`);
153
+ }
154
+ }
155
+ if (p.membersOrder !== undefined &&
156
+ p.membersOrder !== "source" &&
157
+ p.membersOrder !== "alphabetical") {
158
+ throw new Error(`autodoc.python.membersOrder must be "source" or "alphabetical" ` +
159
+ `in ${sourcePath}; got ${JSON.stringify(p.membersOrder)}`);
160
+ }
161
+ if (p.headingLevel !== undefined && typeof p.headingLevel !== "number") {
162
+ throw new Error(`autodoc.python.headingLevel must be a number in ${sourcePath}`);
163
+ }
164
+ if (p.filters !== undefined) {
165
+ if (!Array.isArray(p.filters) ||
166
+ p.filters.some((f) => typeof f !== "string")) {
167
+ throw new Error(`autodoc.python.filters must be an array of strings in ${sourcePath}`);
168
+ }
169
+ }
170
+ return { python: p };
171
+ }
116
172
  function validatePlugins(raw, sourcePath) {
117
173
  if (raw === undefined)
118
174
  return undefined;
@@ -75,6 +75,7 @@ export async function resolveAutodocRefs(pages, options) {
75
75
  for (const page of pages) {
76
76
  await walkAndResolve(page.tree, {
77
77
  configDir: options.configDir,
78
+ pythonOptions: options.pythonOptions,
78
79
  md,
79
80
  onResolved: () => {
80
81
  resolved++;
@@ -187,16 +188,21 @@ async function resolveOne(node, ctx) {
187
188
  // Resolve against configDir so the resolver doesn't depend on
188
189
  // process.cwd().
189
190
  const sourceRoot = resolvePath(ctx.configDir, sourceRootRaw);
190
- // Pull rendering options from props, layered on top of the
191
- // mkdocstrings-compatible defaults. tree-builder.ts uses
192
- // truthy-checks for several of these (e.g. `if (options.showSource
193
- // && symbol.sourceText) { ... }`) — without the defaults the
194
- // collapsible source-code block, attribute signatures, and class
195
- // bases all silently drop. The defaults mirror format-mkdocs/
196
- // src/loader.ts processAutodocDirectives so the migrated
197
- // output matches the legacy import-mkdocs output by default.
191
+ // Layer rendering options, lowest highest precedence:
192
+ // 1. AUTODOC_RENDER_DEFAULTS — bare mkdocstrings defaults.
193
+ // tree-builder.ts truthy-checks several of these, so without
194
+ // them the collapsible source block, attribute signatures,
195
+ // and class bases silently drop.
196
+ // 2. ctx.pythonOptions project-level `config.autodoc.python`,
197
+ // lifted by migrate-mkdocs from the source mkdocs.yml's
198
+ // mkdocstrings handler (members_order, merge_init_into_class,
199
+ // …). This is what makes a migrated reference page match the
200
+ // original instead of the bare-defaults render.
201
+ // 3. props — the per-directive `:::autodoc` YAML body, so an
202
+ // individual symbol can still override the project default.
198
203
  const renderOptions = {
199
204
  ...AUTODOC_RENDER_DEFAULTS,
205
+ ...(ctx.pythonOptions ?? {}),
200
206
  ...props,
201
207
  };
202
208
  delete renderOptions.ref;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dogsbay",
3
- "version": "0.2.0-beta.42",
3
+ "version": "0.2.0-beta.43",
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": {
@@ -32,15 +32,15 @@
32
32
  "picocolors": "^1.1.0",
33
33
  "prompts": "^2.4.2",
34
34
  "yaml": "^2.8.3",
35
- "@dogsbay/autodoc-python": "0.2.0-beta.42",
36
- "@dogsbay/format-mkdocs": "0.2.0-beta.42",
37
- "@dogsbay/format-obsidian": "0.2.0-beta.42",
38
- "@dogsbay/format-astro": "0.2.0-beta.42",
39
- "@dogsbay/format-mdx": "0.2.0-beta.42",
40
- "@dogsbay/format-starlight": "0.2.0-beta.42",
41
- "@dogsbay/format-dogsbay-md": "0.2.0-beta.42",
42
- "@dogsbay/format-openapi": "0.2.0-beta.42",
43
- "@dogsbay/types": "0.2.0-beta.42"
35
+ "@dogsbay/autodoc-python": "0.2.0-beta.43",
36
+ "@dogsbay/format-mkdocs": "0.2.0-beta.43",
37
+ "@dogsbay/format-astro": "0.2.0-beta.43",
38
+ "@dogsbay/format-obsidian": "0.2.0-beta.43",
39
+ "@dogsbay/format-mdx": "0.2.0-beta.43",
40
+ "@dogsbay/format-starlight": "0.2.0-beta.43",
41
+ "@dogsbay/format-dogsbay-md": "0.2.0-beta.43",
42
+ "@dogsbay/format-openapi": "0.2.0-beta.43",
43
+ "@dogsbay/types": "0.2.0-beta.43"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/markdown-it": "^14.1.0",