Skip to content

Fix macro resolve ordering when macros depend on other macros#1756

Open
MarcelOlsen wants to merge 4 commits intoelysiajs:mainfrom
MarcelOlsen:fix/macro-ordering
Open

Fix macro resolve ordering when macros depend on other macros#1756
MarcelOlsen wants to merge 4 commits intoelysiajs:mainfrom
MarcelOlsen:fix/macro-ordering

Conversation

@MarcelOlsen
Copy link
Copy Markdown
Contributor

@MarcelOlsen MarcelOlsen commented Feb 25, 2026

resolves: #1743

Problem

When a macro's resolve function depends on context provided by another macro's resolve, the dependent macro's resolve runs before the dependency. This results in the dependency's values being undefined at runtime.

const sessionsPlugin = new Elysia().macro({
  sessions: {
    resolve: () => ({ sessions: { create: () => {}, get: () => {}, delete: () => {} } }),
  },
});

const authPlugin = new Elysia()
  .use(sessionsPlugin)
  .macro({
    auth: {
      resolve: ({ sessions }) => {
        sessions.get(); // runtime error: sessions is undefined
        return { auth: { currentUser: null } };
      },
      sessions: true,
    },
  });

Root Cause

In applyMacro(), the inner loop iterates over Object.entries(macroHook) in JavaScript insertion order. For auth: { resolve: fn, sessions: true }:

  1. k = 'resolve' - auth.resolve is appended to localHook first.
  2. k = 'sessions' - recursive expansion adds sessions.resolve afterward.

Result: localHook.resolve = [auth.resolve, sessions.resolve] - incorrect order.

Fix

Split the single inner loop into two passes:

  1. Pass 1: Process entries where k is in macro (recursive macro expansion) - dependency resolves are added first.
  2. Pass 2: Process everything else (schema, introspect, detail, hooks like resolve/beforeHandle/derive) - appended after dependencies.

This produces localHook.resolve = [sessions.resolve, auth.resolve] regardless of property declaration order.

Summary by CodeRabbit

  • New Features

    • Added a new discriminated macro entry type and enhanced macro typings to provide finer-grained, flag-aware type inference and propagate macro context into route typings.
  • Refactor

    • Improved macro expansion to a two-pass approach for more reliable ordering and reduced redundant work during application.
  • Tests

    • Added extensive tests covering macro dependency resolution across plugins, multi-level chains, parallel dependencies, deduplication, and guard/group behaviors (sync and async).

@MarcelOlsen
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

Adds discriminated macro typing and MacroContext propagation, refactors runtime macro expansion in src/index.ts to a two-pass cached-entries algorithm, exposes a new public type DiscriminatedMacroEntry, and adds extensive tests validating macro resolve dependency ordering and cross-plugin resolution scenarios.

Changes

Cohort / File(s) Summary
Core macro runtime
src/index.ts
Refactors applyMacro to a two-pass expansion using cached Object.entries: first expands nested macro keys recursively and removes raw flags, second processes remaining keys (validators, seed/introspect/detail, derive/resolve). Replaces in-loop re-expansion with cached iteration and consolidates macro handling paths. Also updates exported declarations to include DiscriminatedMacroEntry.
Macro typing
src/types.ts
Adds DiscriminatedMacroEntry, MacroEntryForSelected, SingleKeyMacroEntries types and updates RouteSchemaWithResolvedMacro to include resolve. Expands macro-related type overloads to propagate MacroContext and resolve typings.
Macro tests (behavior)
test/macro/macro.test.ts
Adds extensive "Macro resolve dependency ordering" tests covering cross-plugin resolution, multi-level chains, parallel dependencies, deduplication, guard/group interactions, async vs sync resolution, cycle handling, and related scenarios.
Macro tests (types)
test/types/macro.ts
Adds cross-plugin macro type tests demonstrating dependent macro resolution across plugins with object and named macro variants and asserts resolved shapes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble flags and hop through keys,
Two-pass leaps and cached entries, please.
Types now whisper which macro is true,
Tests parade the chain and cue.
A rabbit cheers — macros made anew. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: resolving macro execution order when macros depend on other macros. It directly corresponds to the core change of implementing a two-pass expansion to ensure dependency resolves execute before dependent resolves.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can use your project's `biome` configuration to improve the quality of JS/TS/CSS/JSON code reviews.

Add a configuration file to your project to customize how CodeRabbit runs biome.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/macro/macro.test.ts`:
- Around line 1593-1625: The test "deduplication preserved with shared
dependencies" records baseCallCount but never asserts it; update the test inside
the same it block (the one that builds the Elysia instance with .macro and calls
app.handle) to assert that baseCallCount equals 1 after awaiting the response
JSON so the test explicitly verifies that the shared dependency resolver (the
base resolve function) was executed only once.
- Line 1481: The test contains a no-op assertion because it references a
misspelled field `hasessions` and does not call the matcher; update the
assertion on the `response` object to use the correct property name
(`hasSessions`) and invoke the matcher method (e.g., change the call to
expect(response.hasSessions).not.toBeUndefined() or another appropriate matcher
like toBeTruthy()) so the test actually validates the presence of the
cross-plugin dependency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbaf6b7 and e5b69e6.

📒 Files selected for processing (2)
  • src/index.ts
  • test/macro/macro.test.ts

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 25, 2026

Open in StackBlitz

npm i https://pkg.pr.new/elysia@1756

commit: 34cdbb3

@MarcelOlsen
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@MarcelOlsen
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@MarcelOlsen MarcelOlsen marked this pull request as ready for review March 16, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested macros not working

1 participant