Skip to content

feat(mdxish): support library imports in declarations#1530

Merged
kevinports merged 1 commit into
nextfrom
dimas/rm-16919-support-importing-libraries
Jul 2, 2026
Merged

feat(mdxish): support library imports in declarations#1530
kevinports merged 1 commit into
nextfrom
dimas/rm-16919-support-importing-libraries

Conversation

@eaglethrost

@eaglethrost eaglethrost commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
🎫 Resolve RM-16919

🎯 What does this PR do?

Lets in-document declarations use import statements. Previously an imported name (e.g. React hooks) was dropped, so using it threw <name> is not defined. import declarations now resolve against a module registry (React by default) and their values are injected into the evaluation scope.

Not in scope but can be revisited:

  • No path to pass in imports to the engine yet
  • Currently the default available module is only "React" as that's what is was in MDX & the most commonly used one. We can consider extending this. Only React imports work for now

🧪 QA tips

Try these in an MDXish doc — each should render without a "not defined" error:

Named import (the reported case):

import { useState } from 'react';

export const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Add</button>
      <p>{count}</p>
    </div>
  );
};

<Counter />

Default / namespace / aliased imports:

import React from 'react';
import * as ReactLib from 'react';
import { useState as useLocalState } from 'react';

export const A = () => React.createElement('span', null, 'a');
export const B = () => ReactLib.createElement('span', null, 'b');
export const C = () => { const [n] = useLocalState(7); return <em>{n}</em>; };

<A /> <B /> <C />

Unsupported library - warns and skips, rest of the doc still renders:

import { clamp } from 'lodash';

export const greeting = "hi";

{greeting}

📸 Screenshot or Loom

Screen.Recording.2026-07-02.at.5.58.30.pm.mov

@eaglethrost eaglethrost changed the title feat(mdxish): support library imports in declarations feat(mdxish): support library imports in declarations for React Jul 2, 2026
@eaglethrost eaglethrost changed the title feat(mdxish): support library imports in declarations for React feat(mdxish): support library imports in declarations Jul 2, 2026
@eaglethrost eaglethrost requested a review from a team July 2, 2026 07:59
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds support for resolving ESM import statements within MDXish documents that contain export declarations. A new module, resolve-esm-imports.ts, defines a module registry (pre-configured with React) and a collectImportValues function that maps import bindings (default, namespace, named, aliased) to resolved values, warning on unsupported libraries. evaluate-exports.ts is updated to collect ImportDeclaration nodes separately from export declarations and pass resolved import values into the evaluation sandbox alongside React. Tests are added covering import resolution scenarios, including safeMode behavior where imports remain unresolved.

Changes

Area Description
resolve-esm-imports.ts New module: ModuleRegistry type, defaultModuleRegistry, collectImportValues function resolving import bindings from a registry
evaluate-exports.ts Collects ImportDeclaration nodes separately, passes resolved import values into evaluation sandbox
exports.test.ts New test suite for library import resolution, rendering behavior, and safeMode handling

Sequence Diagram(s)

sequenceDiagram
  participant EvaluateExports
  participant CollectImportValues
  participant EvaluateSandbox
  EvaluateExports->>EvaluateExports: traverse estreeBody nodes
  EvaluateExports->>EvaluateExports: collect ImportDeclaration nodes
  EvaluateExports->>CollectImportValues: importDeclarations
  CollectImportValues-->>EvaluateExports: importValues (resolved bindings)
  EvaluateExports->>EvaluateSandbox: evaluate(program, {React, ...importValues})
  EvaluateSandbox-->>EvaluateExports: exported scope
Loading

Related PRs: None identified.

Suggested labels: enhancement, mdxish

Suggested reviewers: None identified.

🐰 A rabbit hops through import lines,
Binding React where export shines,
Unsupported libs get a gentle warn,
New tests confirm what's newly born,
SafeMode keeps the code inert and fine.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
__tests__/lib/mdxish/exports.test.ts (1)

326-335: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test doesn't assert the warning it's named after.

The test title says it "warns," but nothing spies on console.warn to confirm that. It would still pass if the warning behavior regressed.

✅ Suggested addition
     it('warns and skips an unsupported library without breaking the rest of the doc', () => {
+      const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
       const md = `import { clamp } from 'lodash';
 
 export const greeting = "hi";
 
 {greeting}`;
       const html = mix(md);
       expect(html).not.toContain('import {');
       expect(html).toContain('hi');
+      expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('lodash'));
+      warnSpy.mockRestore();
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/lib/mdxish/exports.test.ts` around lines 326 - 335, The test named
in exports.test.ts for handling an unsupported library currently only checks
that mix() skips the import and preserves the rest of the doc, but it never
verifies the “warns” part of the behavior. Update this test to spy on
console.warn around the mix(md) call and assert that a warning is emitted for
the unsupported lodash import, while keeping the existing output assertions
intact so the test covers both the warning and the skip behavior.
processor/transform/mdxish/resolve-esm-imports.ts (1)

15-17: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Silent undefined for unresolved named imports.

getModuleExport returns undefined with no warning when importedName isn't found on the module (e.g., a typo like useStat instead of useState). This produces a confusing downstream TypeError inside evaluate() rather than the clear [WARNING] message already used for unsupported libraries.

♻️ Suggested fix
-const getModuleExport = (module: unknown, importedName: string): unknown =>
-  isRecord(module) && importedName in module ? module[importedName] : undefined;
+const getModuleExport = (module: unknown, importedName: string): unknown => {
+  if (isRecord(module) && importedName in module) return module[importedName];
+  // eslint-disable-next-line no-console
+  console.warn(`[WARNING] "${importedName}" is not exported by the resolved module.`);
+  return undefined;
+};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@processor/transform/mdxish/resolve-esm-imports.ts` around lines 15 - 17, The
named-export lookup in getModuleExport currently returns undefined silently when
importedName is missing, which delays the failure until evaluate() throws a less
helpful error. Update the resolve-esm-imports flow so unresolved named imports
are detected at the lookup point and routed through the existing warning/error
handling used for unsupported libraries, with a clear message that includes the
missing importedName and the module being resolved. Keep the check in
getModuleExport or its immediate caller so evaluate() only receives valid
exports or an explicit warning path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@__tests__/lib/mdxish/exports.test.ts`:
- Around line 326-335: The test named in exports.test.ts for handling an
unsupported library currently only checks that mix() skips the import and
preserves the rest of the doc, but it never verifies the “warns” part of the
behavior. Update this test to spy on console.warn around the mix(md) call and
assert that a warning is emitted for the unsupported lodash import, while
keeping the existing output assertions intact so the test covers both the
warning and the skip behavior.

In `@processor/transform/mdxish/resolve-esm-imports.ts`:
- Around line 15-17: The named-export lookup in getModuleExport currently
returns undefined silently when importedName is missing, which delays the
failure until evaluate() throws a less helpful error. Update the
resolve-esm-imports flow so unresolved named imports are detected at the lookup
point and routed through the existing warning/error handling used for
unsupported libraries, with a clear message that includes the missing
importedName and the module being resolved. Keep the check in getModuleExport or
its immediate caller so evaluate() only receives valid exports or an explicit
warning path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e22ea668-a0f6-4a82-a299-e52a7107a621

📥 Commits

Reviewing files that changed from the base of the PR and between 945fbf9 and 6e5477f.

📒 Files selected for processing (3)
  • __tests__/lib/mdxish/exports.test.ts
  • processor/transform/mdxish/evaluate-exports.ts
  • processor/transform/mdxish/resolve-esm-imports.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • readmeio/ai (manual)
  • readmeio/gitto (manual)
  • readmeio/markdown (manual)
  • readmeio/readme (manual)

@kevinports kevinports merged commit fa6ee97 into next Jul 2, 2026
8 checks passed
@kevinports kevinports deleted the dimas/rm-16919-support-importing-libraries branch July 2, 2026 13:37
rafegoldberg pushed a commit that referenced this pull request Jul 2, 2026
## Version 14.11.0
### ✨ New & Improved

* **mdxish:** replace brace-escaping preprocessing with lenient expression tokenizer ([#1531](#1531)) ([5c5a57c](5c5a57c))
* **mdxish:** support library imports in declarations ([#1530](#1530)) ([fa6ee97](fa6ee97))

### 🛠 Fixes & Updates

* correct exports map ordering ([#1529](#1529)) ([08a63dd](08a63dd))

<!--SKIP CI-->
@rafegoldberg

Copy link
Copy Markdown
Collaborator

This PR was released!

🚀 Changes included in v14.11.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants