Skip to content

fix: get mdc configs by calling mdc:configSources hook#3736

Open
danielroe wants to merge 1 commit intomainfrom
fix/mdc-config-order
Open

fix: get mdc configs by calling mdc:configSources hook#3736
danielroe wants to merge 1 commit intomainfrom
fix/mdc-config-order

Conversation

@danielroe
Copy link
Member

@danielroe danielroe commented Feb 27, 2026

🔗 Linked issue

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

discovered whilst working on nuxt-content-twoslash, currently if a module also hooks into mdc:configSources but does so after nuxt content, any changes it makes will be silently dropped due to ordering.

this instead moves to call the hook (and depends on nuxt-content/mdc#471)

Important

this should not be released before nuxt-content/mdc#471. the mdc pr is safe to release on its own, but it should be released, and then this PR should be released, depending on the newer version of mdc, or we'll lose 'local' mdc.config.ts files

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.
@vercel
Copy link

vercel bot commented Feb 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
content Ready Ready Preview, Comment Feb 27, 2026 9:56am
@danielroe danielroe requested a review from farnabaz February 27, 2026 08:00
@danielroe danielroe marked this pull request as draft February 27, 2026 08:00
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 27, 2026

npm i https://pkg.pr.new/@nuxt/content@3736

commit: ebcba9b

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

The changes convert MDC configuration handling from eager, synchronous parsing to a lazy, asynchronous resolver pattern. The exported setParserOptions function is replaced with setMdcConfigResolver, which accepts an asynchronous callback. Configuration sources are now collected via Nuxt hook and lazily imported on first resolver invocation, with results cached for subsequent calls. Parser initialization no longer occurs during module setup but is deferred until configs are actually needed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: switching from reactive MDC config handling to actively calling the mdc:configSources hook.
Description check ✅ Passed The description is directly related to the changeset, explaining the bug fix motivation and the problem being solved with MDC config ordering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/mdc-config-order

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@danielroe danielroe force-pushed the fix/mdc-config-order branch from 8547381 to a426486 Compare February 27, 2026 09:51
@danielroe danielroe changed the title fix: process content on nuxt.ready rather than in modules:done Feb 27, 2026
@danielroe danielroe marked this pull request as ready for review February 27, 2026 11:13
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/utils/mdc.ts (1)

19-30: Prevent duplicate config resolution on concurrent first access.

Lines 21-27 can run more than once if multiple calls happen before _configs is set. Cache the in-flight promise too.

♻️ Suggested change
 let _configs: MdcConfig[] | undefined
+let _configsPromise: Promise<MdcConfig[]> | undefined
 setMdcConfigResolver(async () => {
-  if (!_configs) {
-    const paths: string[] = []
-    await nuxt.callHook('mdc:configSources', paths)
-    const jiti = createJiti(nuxt.options.rootDir)
-    _configs = paths.length
-      ? await Promise.all(paths.map(path => jiti.import(path).then(m => (m as { default: MdcConfig }).default || m)))
-      : []
-  }
-  return _configs
+  if (_configs) {
+    return _configs
+  }
+  _configsPromise ||= (async () => {
+    const paths: string[] = []
+    await nuxt.callHook('mdc:configSources', paths)
+    const jiti = createJiti(nuxt.options.rootDir)
+    _configs = paths.length
+      ? await Promise.all(paths.map(path => jiti.import(path).then(m => (m as { default: MdcConfig }).default || m)))
+      : []
+    return _configs
+  })()
+  return _configsPromise
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/mdc.ts` around lines 19 - 30, The resolver passed to
setMdcConfigResolver can run multiple times concurrently because only the
resolved _configs is cached; instead cache the in-flight promise so concurrent
calls await the same work. Modify the resolver in src/utils/mdc.ts to use a
module-level variable (e.g., _configsPromise) and set it when starting the async
work (calling nuxt.callHook('mdc:configSources'), createJiti(...) and
Promise.all(paths.map(... jiti.import ...))) and have subsequent calls return
await _configsPromise; once fulfilled assign the resulting array to _configs and
clear/retain the promise as appropriate so only the first invocation does the
import work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/mdc.ts`:
- Around line 23-26: The call to nuxt.callHook('mdc:configSources', paths) uses
an unreleased feature (PR `#471`) so paths may remain empty with
`@nuxtjs/mdc`@0.20.1; update the call to be defensive: detect the installed mdc
version or the presence of the hook support and only call
nuxt.callHook('mdc:configSources', paths) when supported, otherwise fall back to
the existing default sources or explicitly populate paths; reference the symbols
nuxt.callHook, 'mdc:configSources', and the paths variable in src/utils/mdc.ts,
or alternatively update the dependency constraint to a version that includes PR
`#471` before relying on the hook.

---

Nitpick comments:
In `@src/utils/mdc.ts`:
- Around line 19-30: The resolver passed to setMdcConfigResolver can run
multiple times concurrently because only the resolved _configs is cached;
instead cache the in-flight promise so concurrent calls await the same work.
Modify the resolver in src/utils/mdc.ts to use a module-level variable (e.g.,
_configsPromise) and set it when starting the async work (calling
nuxt.callHook('mdc:configSources'), createJiti(...) and
Promise.all(paths.map(... jiti.import ...))) and have subsequent calls return
await _configsPromise; once fulfilled assign the resulting array to _configs and
clear/retain the promise as appropriate so only the first invocation does the
import work.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8547381 and a426486.

📒 Files selected for processing (2)
  • src/utils/content/index.ts
  • src/utils/mdc.ts
Comment on lines +23 to +26
await nuxt.callHook('mdc:configSources', paths)
const jiti = createJiti(nuxt.options.rootDir)
const configs = await Promise.all(mdcConfigs.map(path => jiti.import(path).then(m => (m as { default: MdcConfig }).default || m)))

setParserOptions({
mdcConfigs: configs,
})
_configs = paths.length
? await Promise.all(paths.map(path => jiti.import(path).then(m => (m as { default: MdcConfig }).default || m)))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json, glob
sections = ("dependencies", "peerDependencies", "devDependencies", "optionalDependencies")
print("Found `@nuxtjs/mdc` version constraints:")
for f in glob.glob("**/package.json", recursive=True):
    try:
        data = json.load(open(f))
    except Exception:
        continue
    for s in sections:
        v = (data.get(s) or {}).get("@nuxtjs/mdc")
        if v:
            print(f"{f} :: {s} -> {v}")
PY

for lock in pnpm-lock.yaml package-lock.json yarn.lock; do
  if [ -f "$lock" ]; then
    echo
    echo "Entries in $lock:"
    rg -n '@nuxtjs/mdc' "$lock" || true
  fi
done

Repository: nuxt/content

Length of output: 361


🌐 Web query:

@nuxtjs/mdc mdc:configSources hook release version history PR 471

💡 Result:

  • Hook: mdc:configSources is a Nuxt hook exposed by @nuxtjs/mdc with signature (configs: string[]) => void (i.e., modules can mutate/extend the list of MDC config source files). It’s present at least as far back as @nuxtjs/mdc@0.13.1 (in the published type declarations). [3]
  • Relevant releases (where behavior around the hook is explicitly mentioned):
    • v0.20.0 (Jan 15, 2026): “Call mdc:configSources after all modules have run” (PR Link to edit the page in Github is invalid #455). [2]
    • v0.20.1 (Feb 5, 2026): latest release shown on GitHub Releases at the time of writing (does not mention further mdc:configSources changes). [2]
  • PR #471 (release status): PR feat(theme-docs): add locale simplified chinese support #471 titled “fix: don't memoise configs, and allow external modules to call mdc:configSources” is open as of Feb 27, 2026, and is intended to let external modules call the hook (not just listen) and to avoid memoizing configs, coordinated with nuxt/content#3736. It is not released yet (not included in the v0.20.1 notes). [1]

Sources: PR #471 page [1], nuxt-content/mdc GitHub Releases [2], @nuxtjs/mdc@0.13.1 type declarations (unpkg) [3].


🏁 Script executed:

#!/bin/bash
# Check if the code is using any features that would require PR `#471`
# PR `#471` is about allowing external modules to call the hook and avoiding memoization

# First, let's see the full context around the mdc hook usage
cat -n src/utils/mdc.ts | head -40

Repository: nuxt/content

Length of output: 2008


🌐 Web query:

@nuxtjs/mdc v0.20.1 mdc:configSources hook usage external modules call

💡 Result:

In @nuxtjs/mdc, the Nuxt hook is typed as:

  • mdc:configSources: (configs: string[]) => void (you receive a mutable array of file paths and can push your own config source paths into it). [1]

Use it from an external Nuxt module

// modules/my-mdc-config/module.ts
import { defineNuxtModule, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(_options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    nuxt.hook('mdc:configSources', (configs) => {
      // add your own MDC config source file(s)
      configs.push(resolve('./mdc.config.ts'))
      // configs.push(resolve('./another.mdc.config.ts'))
    })
  }
})

Add your module after @nuxtjs/mdc in nuxt.config.ts so the hook exists when you register:

export default defineNuxtConfig({
  modules: [
    '@nuxtjs/mdc',
    '~/modules/my-mdc-config'
  ]
})

Notes

  • This is a Nuxt (build-time) hook; use it from a Nuxt module (nuxt.hook(...)), not from a runtime plugin.
  • The hook expects paths (string[]), so you typically push resolved absolute paths to config files. [1]

Source(s): @nuxtjs/mdc type declarations showing the hook signature. [1]

References

[1] https://app.unpkg.com/%40nuxtjs/mdc%400.13.1/files/dist/module.d.mts


The mdc:configSources hook call requires unreleased features not available in v0.20.1.

Line 23 calls await nuxt.callHook('mdc:configSources', paths) to allow modules to populate the paths array. However, this requires PR #471 ("allow external modules to call mdc:configSources"), which is currently open and not released. The locked version @nuxtjs/mdc@0.20.1 predates this feature, meaning the hook call will succeed but paths will silently remain empty if no code in v0.20.1 processes it. Either upgrade the version constraint once PR #471 is released, or verify that v0.20.1 supports external hook calls via a different mechanism.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/mdc.ts` around lines 23 - 26, The call to
nuxt.callHook('mdc:configSources', paths) uses an unreleased feature (PR `#471`)
so paths may remain empty with `@nuxtjs/mdc`@0.20.1; update the call to be
defensive: detect the installed mdc version or the presence of the hook support
and only call nuxt.callHook('mdc:configSources', paths) when supported,
otherwise fall back to the existing default sources or explicitly populate
paths; reference the symbols nuxt.callHook, 'mdc:configSources', and the paths
variable in src/utils/mdc.ts, or alternatively update the dependency constraint
to a version that includes PR `#471` before relying on the hook.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant