Skip to content

Conversation

@cernymatej
Copy link
Member

@cernymatej cernymatej commented Oct 10, 2025

🔗 Linked issue

resolves #26275

📚 Description

This is a rework of how automatic key injection for functions like useAsyncData, useState, etc. is handled.
Previously, it wasn't possible to add keyed functions from modules as the build plugin used a snapshot of the config from before they had the chance to update it. This is now possible. With that, I feel like there is a greater need to target the functions in a stricter way.

There was an undocumented (and therefore internal) source option in the keyedComposables object before, which accepted a RegExp in addition to string. This PR removes the RegExp option on the type-level as it doesn't fit the new use case. Also, the source option has now been made required.

This also fixes various issues with the old implementation, where we would inject keys into incorrect functions:

keyedComposables: [{
  name: 'useKey',
  source: '#app',
  argumentLength: 1,
}]
import { useKey } from '#application' // deliberately using an alias starting with #app
useKey()  // would get transformed to `useKey('$HJiaryoL2y')`

or wouldn't handle renaming in imports:

import { useKey as useRenamedKey } from '#app'
useRenamedKey() // no transformation would be performed

wouldn't handle default exports

keyedComposables: [{ name: 'default', /* ... */

and more cases for calls in the source files...

These changes are a foundation for #32300. (more of the compiler module is implemented there) This PR should go first and then I'll rebase the one with the factories.

🚧 TODO

  • resolve quirks with default exports
  • test implementation with undefined source (in this case, it should inject to auto-imported functions)

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 10, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33446

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@33446

nuxt

npm i https://pkg.pr.new/nuxt@33446

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33446

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33446

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33446

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33446

commit: 86e31a0

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 10, 2025

CodSpeed Performance Report

Merging #33446 will improve performance by 11.73%

Comparing cernymatej:refactor/ast-keyed-functions (86e31a0) with main (6412590)1

Summary

⚡ 1 improvement
✅ 9 untouched

Benchmarks breakdown

Benchmark BASE HEAD Efficiency
writeTypes in the basic-types fixture 89 ms 79.6 ms +11.73%

Footnotes

  1. No successful run was found on main (b5c839d) during the generation of this report, so 6412590 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@cernymatej
Copy link
Member Author

cernymatej commented Oct 11, 2025

@danielroe after giving it some thought, I think it would be much better to make the source property required now. Without it, default exports cannot be specified, and also - what is a much bigger problem - now that the plugin runs after the auto-imports plugin (as the imports plugin is added in the imports module, which runs before to allow modules to modify keyedComposables), we can't tell when a function is auto-imported and when not.

the previous implementation injected keys when the matching functions were imported from #imports or auto-imported

in the current implementation, keyedComposables entries without the source specified are skipped... 😕


would it be acceptable to make such change in a minor?

@cernymatej cernymatej marked this pull request as ready for review October 11, 2025 22:11
@cernymatej cernymatej requested a review from danielroe as a code owner October 11, 2025 22:11
@coderabbitai
Copy link

coderabbitai bot commented Oct 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new runtime entry compiler to the build config. Introduces parsing utilities at packages/nuxt/src/compiler/parse-utils.ts for static import, function-call and export identifier analysis. Adds a new keyed-functions unplugin at packages/nuxt/src/compiler/plugins/keyed-functions.ts and removes the previous composable-keys plugin, wiring the new plugin into Nuxt core with Unimport integration. Adds synchronous filesystem and string utilities (isDirectorySync, stripExtension, isWhitespace, DECLARATION_EXTENSIONS) and updates components module to use them. Extends schema/types with KeyedFunction and updates keyedComposables. Adds tests for parser and keyed-functions and removes the old composable-keys tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Potential attention areas:

  • packages/nuxt/src/compiler/plugins/keyed-functions.ts: complex AST/code-transform logic, sourcemap handling and import/namespace resolution.
  • packages/nuxt/src/compiler/parse-utils.ts and tests: numerous parsing edge-cases and TypeScript-specific AST handling.
  • Core wiring in packages/nuxt/src/core/nuxt.ts: integration with Unimport and replacement of previous plugin.
  • Removal of packages/nuxt/src/core/plugins/composable-keys.ts: ensure no remaining references and behaviour parity with new plugin.
  • Schema/type changes (packages/schema/*): public API/type updates affecting config typing and downstream consumers.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor(nuxt): use AST-aware function key injection' is clear and specific, directly describing the main architectural change from the old snapshot-based approach to an AST-aware implementation for keyed composables.
Description check ✅ Passed The description comprehensively explains the purpose (fixing key injection for keyed composables added by modules), the approach (AST-aware targeting), the breaking changes (source made required, RegExp removed), and references the linked issue #26275 with implementation details.
Linked Issues check ✅ Passed The PR fully addresses issue #26275 by implementing AST-aware function key injection that correctly handles explicitly imported composables in modules, renamed imports, default exports, and strict source matching to prevent incorrect transformations.
Out of Scope Changes check ✅ Passed All changes are in scope: parser utilities and keyed-functions plugin support the new AST-aware approach, configuration changes reflect the source requirement and type updates, test suites validate the new implementation, and utility functions support the compiler infrastructure.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34d3c55 and c7857c6.

📒 Files selected for processing (4)
  • packages/nuxt/src/components/module.ts (3 hunks)
  • packages/nuxt/src/core/nuxt.ts (3 hunks)
  • packages/schema/src/index.ts (1 hunks)
  • packages/schema/src/types/schema.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/schema/src/types/schema.ts
  • packages/nuxt/src/components/module.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/schema/src/index.ts
  • packages/nuxt/src/core/nuxt.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: GalacticHypernova
Repo: nuxt/nuxt PR: 26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
Repo: nuxt/nuxt PR: 26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/core/nuxt.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
Repo: nuxt/nuxt PR: 29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.

Applied to files:

  • packages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (1)
packages/nuxt/src/core/nuxt.ts (3)
packages/kit/src/resolve.ts (1)
  • resolvePath (53-64)
packages/kit/src/build.ts (1)
  • addBuildPlugin (212-224)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (52-408)
🔇 Additional comments (1)
packages/schema/src/index.ts (1)

4-4: LGTM!

The type export is correctly formed, follows the established pattern in the file, and maintains alphabetical ordering. The addition of KeyedFunction to the public API appropriately supports the new AST-aware function key injection feature.


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.

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: 0

🧹 Nitpick comments (2)
packages/nuxt/src/components/module.ts (1)

112-114: Drop the empty brace alternative in the declaration ignore pattern.

Line 112 expands to **/*.{d.ts,d.mts,d.cts,}, which leaves an empty alternative and causes the ignore set to match files ending in a bare dot. Remove the trailing comma so only the declaration extensions are ignored.

-            `**/*.{${DECLARATION_EXTENSIONS.join(',')},}`, // .d.ts files
+            `**/*.{${DECLARATION_EXTENSIONS.join(',')}}`, // .d.ts files
packages/schema/src/types/compiler.ts (1)

6-11: Retain source as optional until Nuxt 5, but add a runtime warning for missing entries
All built-in defaults and tests already specify source, so making it required now would be a breaking change reserved for Nuxt 5. To prevent silent skips, emit a warning (or throw) when any keyedComposables entry omits source at runtime, with a pointer to the migration guide.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 002c92c and 679d2a7.

📒 Files selected for processing (15)
  • packages/nuxt/build.config.ts (1 hunks)
  • packages/nuxt/src/compiler/parse-utils.ts (1 hunks)
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts (1 hunks)
  • packages/nuxt/src/components/module.ts (3 hunks)
  • packages/nuxt/src/core/nuxt.ts (3 hunks)
  • packages/nuxt/src/core/plugins/composable-keys.ts (0 hunks)
  • packages/nuxt/src/core/utils/plugins.ts (0 hunks)
  • packages/nuxt/src/utils.ts (2 hunks)
  • packages/nuxt/test/compiler.test.ts (1 hunks)
  • packages/nuxt/test/composable-keys.test.ts (0 hunks)
  • packages/nuxt/test/keyed-functions.test.ts (1 hunks)
  • packages/schema/src/config/build.ts (1 hunks)
  • packages/schema/src/index.ts (1 hunks)
  • packages/schema/src/types/compiler.ts (1 hunks)
  • packages/schema/src/types/schema.ts (2 hunks)
💤 Files with no reviewable changes (3)
  • packages/nuxt/test/composable-keys.test.ts
  • packages/nuxt/src/core/plugins/composable-keys.ts
  • packages/nuxt/src/core/utils/plugins.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/schema/src/index.ts
  • packages/schema/src/types/compiler.ts
  • packages/nuxt/src/components/module.ts
  • packages/nuxt/src/core/nuxt.ts
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts
  • packages/nuxt/src/compiler/parse-utils.ts
  • packages/schema/src/config/build.ts
  • packages/schema/src/types/schema.ts
  • packages/nuxt/build.config.ts
  • packages/nuxt/test/compiler.test.ts
  • packages/nuxt/src/utils.ts
  • packages/nuxt/test/keyed-functions.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • packages/nuxt/test/compiler.test.ts
  • packages/nuxt/test/keyed-functions.test.ts
🧠 Learnings (1)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/components/module.ts
  • packages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (8)
packages/schema/src/types/compiler.ts (1)
packages/schema/src/index.ts (1)
  • KeyedFunction (4-4)
packages/nuxt/src/components/module.ts (1)
packages/nuxt/src/utils.ts (2)
  • isDirectorySync (14-16)
  • DECLARATION_EXTENSIONS (28-28)
packages/nuxt/src/core/nuxt.ts (3)
packages/kit/src/index.ts (2)
  • resolvePath (35-35)
  • addBuildPlugin (21-21)
packages/kit/src/resolve.ts (1)
  • resolvePath (52-63)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (45-362)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-28)
packages/nuxt/src/utils.ts (3)
  • stripExtension (18-20)
  • logger (30-30)
  • isWhitespace (22-26)
packages/nuxt/src/compiler/parse-utils.ts (3)
  • processImports (15-77)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
packages/nuxt/src/compiler/parse-utils.ts (1)
packages/nuxt/src/utils.ts (1)
  • stripExtension (18-20)
packages/schema/src/types/schema.ts (1)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-28)
packages/nuxt/test/compiler.test.ts (1)
packages/nuxt/src/compiler/parse-utils.ts (4)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
  • ExportMetadata (237-240)
  • parseStaticExportIdentifiers (261-361)
packages/nuxt/test/keyed-functions.test.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-28)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (45-362)
packages/nuxt/src/utils.ts (1)
  • logger (30-30)
🪛 ast-grep (0.39.6)
packages/nuxt/src/compiler/plugins/keyed-functions.ts

[warning] 39-39: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\.(${extensions.join('|')})$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 102-102: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 205-205: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...possibleLocalFunctionNames].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-benchmark
🔇 Additional comments (2)
packages/schema/src/types/compiler.ts (1)

19-25: LGTM! Clear and helpful documentation with practical examples.

The documentation for argumentLength effectively explains the transformation behaviour with concrete examples that demonstrate when keys are injected and when they are not.

packages/schema/src/types/schema.ts (1)

42-42: LGTM! Clean refactor to use the centralised KeyedFunction type.

The import and usage of the KeyedFunction type improves code organisation by consolidating the type definition in a single location (./compiler), making it reusable and easier to maintain.

Also applies to: 507-507

@danielroe
Copy link
Member

danielroe commented Oct 12, 2025

would you take a look at the failing webpack/rspack tests? 🙏

I think it's fine to make source required going forward (e.g. in types), but we should fall back to old behaviour if it is absent.

there are only a few examples of modules using the old behaviour:

https://github.com/search?q=keyedComposables+-is%3Afork&type=code

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 679d2a7 and 2539d2e.

📒 Files selected for processing (3)
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts (1 hunks)
  • packages/nuxt/test/keyed-functions.test.ts (1 hunks)
  • packages/schema/src/types/compiler.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/schema/src/types/compiler.ts
  • packages/nuxt/test/keyed-functions.test.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/compiler/plugins/keyed-functions.ts
🧬 Code graph analysis (1)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-27)
packages/nuxt/src/utils.ts (3)
  • stripExtension (18-20)
  • logger (30-30)
  • isWhitespace (22-26)
packages/nuxt/src/compiler/parse-utils.ts (4)
  • processImports (15-77)
  • parseStaticExportIdentifiers (261-361)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
🪛 ast-grep (0.39.6)
packages/nuxt/src/compiler/plugins/keyed-functions.ts

[warning] 40-40: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\.(${extensions.join('|')})$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 94-94: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 197-197: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...possibleLocalFunctionNames].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: test-benchmark
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: test-size
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: code

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: 0

🧹 Nitpick comments (9)
packages/schema/src/types/compiler.ts (1)

1-27: Document default-export semantics and note source requirement

Please extend JSDoc to clarify:

  • Set name: 'default' to target a module’s default export (the injected callable will be the default import; the effective callable name is derived from the filename).
  • source is now required (no RegExp/undefined). Add a brief migration note to avoid confusion.

Example patch:

 export interface KeyedFunction {
   /**
    * The name of the function.
+   * Use 'default' to target a module's default export. In that case, the callable name
+   * is derived from the filename (camel-cased) for matching during analysis.
    */
   name: string
   /**
    * The path to the file where the function is defined.
    * You can use Nuxt aliases (~ or @) to refer to directories inside the project or directly use an npm package path similar to require.
+   *
+   * Note: `source` is required. Previous support for RegExp/undefined is removed.
    */
   source: string
   /**
    * The maximum number of arguments the function can accept.
    * In the case that the function is called with fewer arguments than this number,
    * the compiler will inject an auto-generated key as an additional argument.
    *
    * The key is unique based on the location of the function being invoked within the file.
    *
    * @example `{ name: 'useKey', source: '~/composables/useKey', argumentLength: 2 }`
    *
    * ```ts
    * useKey()                  // will be transformed to: useKey('\$KzLSZ0O59L')
    * useKey('first')           // will be transformed to: useKey('first', '\$KzLSZ0O59L')
    * useKey('first', 'second') // will not be transformed
    * ```
    */
   argumentLength: number
 }
packages/nuxt/src/compiler/plugins/keyed-functions.ts (5)

35-44: Avoid dynamic RegExp from array without escaping; precompute pattern

Current construction may be brittle if extensions contain metacharacters and is flagged for ReDoS by static analysis. Escape and precompile:

-        : new RegExp(`\\.(${extensions.join('|')})$`).test(pathname)
+        : new RegExp(`\\.(?:${extensions.map(e => escapeRE(e)).join('|')})$`).test(pathname)

Import escape-string-regexp if needed in this scope.


94-96: Large dynamic include RegExp: cap/optimise prefilter

This builds a potentially large alternation. Consider:

  • Fallback to a fast string prefilter (code.includes(name)) before the AST pass, or
  • Cap the number of alternations, or
  • Use a Set and a quick scan instead of a single mega-RegExp.

This reduces worst‑case backtracking and memory pressure while preserving correctness since AST analysis is authoritative.


170-189: Parse only the <script> block for SFCs to avoid parse errors

You already extract script and its index (codeIndex). Use script (not full code) as the parse target to avoid attempting to parse .vue markup in edge toolchains where post ordering isn’t guaranteed.

-        const { program } = parseAndWalk(code, id, {
+        const { program } = parseAndWalk(script, id, {
           scopeTracker,
           enter (node) {
             if (!shouldConsiderExports) { return }
             if (node.type !== 'ExportNamedDeclaration' && node.type !== 'ExportDefaultDeclaration') { return }

Position math already accounts for codeIndex, so this change is safe.


352-355: Derive key from callsite location for stability (aligns with docs)

Docs say the key is based on callsite location. Using an incrementing counter can produce different keys across minor AST/order changes. Prefer offsets:

-                (parsedCall.callExpression.arguments.length && !endsWithComma ? ', ' : '') + '\'$' + hash(`${_id}-${++count}`).slice(0, 10) + `' ${NUXT_INJECTED_MARKER}`,
+                (() => {
+                  const seed = `${id}:${parsedCall.callExpression.start}:${parsedCall.callExpression.end}`
+                  const key = '$' + hash(seed).slice(0, 10)
+                  return (parsedCall.callExpression.arguments.length && !endsWithComma ? ', ' : '') + `'${key}' ${NUXT_INJECTED_MARKER}`
+                })(),

Keeps keys deterministic per file + callsite.


321-343: Edge cases for built-in composables: broaden literal detection (optional)

You skip when a literal key already exists. Today you treat any TemplateLiteral as a key (even with expressions). If you want stricter behaviour (only static templates), refine checks to ensure quasis.length === 1 && expressions.length === 0. Optional but improves precision.

packages/nuxt/test/keyed-functions.test.ts (3)

126-138: Make dev-warning test robust to env detection

The plugin guards duplicate warnings behind import.meta.dev. Instead of stubbing a custom global, ensure the test runs with import.meta.dev === true (e.g., via Vitest/Vite config) or temporarily monkey‑patch Object.defineProperty(import.meta, 'dev', { value: true }) if supported. Current vi.stubGlobal('__TEST_DEV__', true) may not affect the guard.


46-57: Add coverage for auto‑imports and SFCs

  • Add a case where a keyed function is auto‑imported (no import statement) to assert the intended behaviour with source required (either inject or deliberately skip).
  • Add a .vue SFC case to ensure parsing of the <script> block works as expected.

As per coding guidelines (Vitest).

Also applies to: 216-223, 523-532


768-799: Strengthen template literal key tests

You currently expect no injection for template literals (including interpolated). If you adopt stricter “static-only” detection, add assertions for both static and interpolated templates to lock behaviour.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 679d2a7 and 3f59c1c.

📒 Files selected for processing (3)
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts (1 hunks)
  • packages/nuxt/test/keyed-functions.test.ts (1 hunks)
  • packages/schema/src/types/compiler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/schema/src/types/compiler.ts
  • packages/nuxt/test/keyed-functions.test.ts
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • packages/nuxt/test/keyed-functions.test.ts
🧬 Code graph analysis (3)
packages/schema/src/types/compiler.ts (1)
packages/schema/src/index.ts (1)
  • KeyedFunction (4-4)
packages/nuxt/test/keyed-functions.test.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-27)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (46-371)
packages/nuxt/src/utils.ts (1)
  • logger (30-30)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-27)
packages/nuxt/src/utils.ts (3)
  • stripExtension (18-20)
  • logger (30-30)
  • isWhitespace (22-26)
packages/nuxt/src/compiler/parse-utils.ts (3)
  • processImports (15-77)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
🪛 ast-grep (0.39.6)
packages/nuxt/src/compiler/plugins/keyed-functions.ts

[warning] 40-40: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\.(${extensions.join('|')})$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 94-94: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 197-197: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...possibleLocalFunctionNames].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: code
🔇 Additional comments (1)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)

111-127: Auto-imported functions (no import node) won’t get keys now

Because importSourceResolved is only set for explicit imports or when processing the exporting source file, calls relying solely on auto-imports (no import statement) are skipped. This matches the new “source required” stance but is a behavioural change.

  • Confirm all module keyedComposables entries specify a concrete source.
  • Consider a fallback mode (optional) for source-less entries if backward compatibility is desired.

@cernymatej
Copy link
Member Author

@danielroe the webpack tests failed because, for some reason, certain files were being transformed more than once.

This has been resolved by adding a /* nuxt-injected */ comment after the injected argument with the key, and checking for this comment before injecting another one (as we discussed on Discord).

I've also added (hopefully full) backward compatibility for cases where source is missing in the keyed composable entry, as well as for when a RegExp was used.

The RegExp support has now been removed on the type-level, and source has been made required.

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

♻️ Duplicate comments (1)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)

144-180: Restore source-less keyed function matching for manual imports.

Line 162 still misses the backwards-compatible fallback for entries without a source. If a module adds { name: 'useFoo', argumentLength: 1 } (no source) and calls import { useFoo } from '~/composables/foo', source is the resolved path while the metadata lives under the empty-string bucket, so getFunctionMetaByLocalName returns undefined and no key is injected. This regresses existing keyed composable configs. Please always fall back to the '' entry (or regex) when the exact source lookup fails, including the exported/local-name branches.

-          if (exportedName) {
-            return namesToSourcesToFunctionMeta.get(exportedName)?.get(source)
+          if (exportedName) {
+            const metaBySource = namesToSourcesToFunctionMeta.get(exportedName)
+            return (source && metaBySource?.get(source)) ?? metaBySource?.get('')
           }
           // check static direct imports
           const directImport = directImports.get(localName)
           if (directImport) {
             const functionName = directImport.originalName === 'default'
               ? camelCase(parse(directImport.source).name)
               : directImport.originalName
-
-            // TODO: remove auto-import checks in Nuxt 5
-            const sourcesToMetas = namesToSourcesToFunctionMeta.get(functionName)
-            if (!sourcesToMetas) { return }
-
-            const fnMeta = sourcesToMetas.get(source)
-            if (fnMeta) { return fnMeta }
-
-            const backwardsCompatibleFnMeta = sourcesToMetas.get('') // functions without a source or with a regex fall under ''
+            const sourcesToMetas = namesToSourcesToFunctionMeta.get(functionName)
+            if (!sourcesToMetas) { return }
+
+            const fnMeta = (source && sourcesToMetas.get(source)) ?? sourcesToMetas.get('')
+            if (!fnMeta) { return }
+
+            if (fnMeta.source === undefined) {
+              return fnMeta
+            }
+
+            const backwardsCompatibleFnMeta = fnMeta
             if (backwardsCompatibleFnMeta?.source === undefined) {
               const autoImportResolvedSource = stripExtension(resolveAlias(autoImportsToSources.get(localName) ?? ''))
               if (autoImportResolvedSource === source) {
                 return backwardsCompatibleFnMeta
               }
             } else if (backwardsCompatibleFnMeta.source instanceof RegExp && backwardsCompatibleFnMeta.source.test(source)) {
               return backwardsCompatibleFnMeta
             }
-
-            return
           }
 
           // check local names
-          return namesToSourcesToFunctionMeta.get(localName)?.get(source)
+          const metaBySource = namesToSourcesToFunctionMeta.get(localName)
+          return (source && metaBySource?.get(source)) ?? metaBySource?.get('')
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0bc7bd and fe1e7b4.

📒 Files selected for processing (4)
  • packages/nuxt/src/compiler/parse-utils.ts (1 hunks)
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts (1 hunks)
  • packages/nuxt/src/core/nuxt.ts (3 hunks)
  • packages/nuxt/test/keyed-functions.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/core/nuxt.ts
  • packages/nuxt/test/keyed-functions.test.ts
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts
  • packages/nuxt/src/compiler/parse-utils.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • packages/nuxt/test/keyed-functions.test.ts
🧬 Code graph analysis (4)
packages/nuxt/src/core/nuxt.ts (3)
packages/kit/src/resolve.ts (1)
  • resolvePath (52-63)
packages/kit/src/build.ts (1)
  • addBuildPlugin (153-165)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (52-408)
packages/nuxt/test/keyed-functions.test.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-30)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)
  • KeyedFunctionsPlugin (52-408)
packages/nuxt/src/utils.ts (1)
  • logger (30-30)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-30)
packages/nuxt/src/utils.ts (3)
  • stripExtension (18-20)
  • logger (30-30)
  • isWhitespace (22-26)
packages/nuxt/src/compiler/parse-utils.ts (4)
  • processImports (15-77)
  • parseStaticExportIdentifiers (261-361)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
packages/nuxt/src/compiler/parse-utils.ts (1)
packages/nuxt/src/utils.ts (1)
  • stripExtension (18-20)
🪛 ast-grep (0.39.6)
packages/nuxt/src/compiler/plugins/keyed-functions.ts

[warning] 43-43: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\.(${extensions.map(e => escapeRE(e)).join('|')})$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 101-101: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 226-226: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...possibleLocalFunctionNames].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: code

@cernymatej cernymatej requested a review from Copilot October 12, 2025 15:08
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors Nuxt's automatic key injection system for functions like useAsyncData, useState, etc., moving from a config-snapshot-based approach to an AST-aware implementation. The refactor enables modules to add keyed functions and ensures proper handling of function imports, exports, and renaming scenarios.

Key Changes:

  • Replaces the old ComposableKeysPlugin with a new KeyedFunctionsPlugin that uses AST analysis
  • Makes the source property required in KeyedFunction interface and removes RegExp support from types
  • Adds comprehensive test coverage for various import/export scenarios

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/schema/src/types/schema.ts Updates keyedComposables type to use new KeyedFunction interface
packages/schema/src/types/compiler.ts Defines new KeyedFunction interface with required source property
packages/schema/src/index.ts Exports new KeyedFunction type
packages/schema/src/config/build.ts Adds source properties to default keyed function configurations
packages/nuxt/test/keyed-functions.test.ts Comprehensive test suite for the new keyed functions plugin
packages/nuxt/test/composable-keys.test.ts Removes old composable keys test file
packages/nuxt/test/compiler.test.ts Adds tests for AST parsing utilities
packages/nuxt/src/utils.ts Adds utility functions for file handling and AST processing
packages/nuxt/src/core/utils/plugins.ts Removes string/regex matching utility function
packages/nuxt/src/core/plugins/composable-keys.ts Removes old composable keys plugin
packages/nuxt/src/core/nuxt.ts Integrates new KeyedFunctionsPlugin with path resolution
packages/nuxt/src/components/module.ts Updates to use new utility functions
packages/nuxt/src/compiler/plugins/keyed-functions.ts New AST-aware keyed functions plugin implementation
packages/nuxt/src/compiler/parse-utils.ts AST parsing utilities for function calls and exports
packages/nuxt/build.config.ts Adds compiler directory to build configuration

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: 0

♻️ Duplicate comments (2)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (2)

144-180: Restore fallback for keyed composables without source.

When a keyed function entry omits source, it's stored under the empty-string key (line 61: fnSource = ''). However, the lookup logic only checks the source-specific entry and never falls back to the '' bucket, causing configurations without source to be skipped. This regression breaks backward compatibility.

The following lookups need the fallback:

  • Line 149: exported name lookup
  • Line 162: direct import lookup
  • Line 179: local name fallback lookup

Apply this diff to restore the fallback behaviour:

 function getFunctionMetaByLocalName (localName: string, source: string): BackwardsCompatibleKeyedFunction | undefined {
   if (!localName) { return }
   // check exports (higher priority)
   const exportedName = localNamesToExportedName.get(localName)
   if (exportedName) {
-    return namesToSourcesToFunctionMeta.get(exportedName)?.get(source)
+    const metaBySource = namesToSourcesToFunctionMeta.get(exportedName)
+    return metaBySource?.get(source) || metaBySource?.get('')
   }
   // check static direct imports
   const directImport = directImports.get(localName)
   if (directImport) {
     const functionName = directImport.originalName === 'default'
       ? camelCase(parse(directImport.source).name)
       : directImport.originalName

     // TODO: remove auto-import checks in Nuxt 5
     const sourcesToMetas = namesToSourcesToFunctionMeta.get(functionName)
     if (!sourcesToMetas) { return }

-    const fnMeta = sourcesToMetas.get(source)
+    const fnMeta = sourcesToMetas.get(source) || sourcesToMetas.get('')
     if (fnMeta) { return fnMeta }

     const backwardsCompatibleFnMeta = sourcesToMetas.get('') // functions without a source or with a regex fall under ''
     if (backwardsCompatibleFnMeta?.source === undefined) {
       const autoImportResolvedSource = stripExtension(resolveAlias(autoImportsToSources.get(localName) ?? ''))
       if (autoImportResolvedSource === source) {
         return backwardsCompatibleFnMeta
       }
     } else if (backwardsCompatibleFnMeta.source instanceof RegExp && backwardsCompatibleFnMeta.source.test(source)) {
       return backwardsCompatibleFnMeta
     }

     return
   }

   // check local names
-  return namesToSourcesToFunctionMeta.get(localName)?.get(source)
+  const metaBySource = namesToSourcesToFunctionMeta.get(localName)
+  return metaBySource?.get(source) || metaBySource?.get('')
 }

69-69: Consider using process.env.NODE_ENV for build-time checks.

import.meta.dev is Vite-specific and may not be reliably available in all build contexts (Webpack, Rspack). For Node.js build plugins, process.env.NODE_ENV is more portable.

Apply this diff:

-    if (import.meta.dev) {
+    if (process.env.NODE_ENV === 'development') {
🧹 Nitpick comments (1)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (1)

346-353: Simplify marker detection with string slice.

The character-by-character comparison can be replaced with a more efficient string slice operation.

Apply this diff:

               let wasKeyInjected = true
-              for (let i = 0; i < NUXT_INJECTED_MARKER.length; i++) {
-                // the magic comment does not match, we have not injected a key yet
-                if (code[codeIndex + lastArgument.end + 1 + i] !== NUXT_INJECTED_MARKER[i]) {
-                  wasKeyInjected = false
-                  break
-                }
-              }
+              const markerStart = codeIndex + lastArgument.end + 1
+              wasKeyInjected = code.slice(markerStart, markerStart + NUXT_INJECTED_MARKER.length) === NUXT_INJECTED_MARKER
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe1e7b4 and d648b1e.

📒 Files selected for processing (1)
  • packages/nuxt/src/compiler/plugins/keyed-functions.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/compiler/plugins/keyed-functions.ts
🧬 Code graph analysis (1)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)
packages/schema/src/types/compiler.ts (1)
  • KeyedFunction (1-30)
packages/nuxt/src/utils.ts (3)
  • stripExtension (18-20)
  • logger (30-30)
  • isWhitespace (22-26)
packages/nuxt/src/compiler/parse-utils.ts (4)
  • processImports (15-77)
  • parseStaticExportIdentifiers (261-361)
  • FunctionCallMetadata (79-104)
  • parseStaticFunctionCall (114-235)
🪛 ast-grep (0.39.6)
packages/nuxt/src/compiler/plugins/keyed-functions.ts

[warning] 43-43: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\.(${extensions.map(e => escapeRE(e)).join('|')})$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 101-101: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 226-226: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${[...possibleLocalFunctionNames].map(f => escapeRE(f)).join('|')})\\b)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: test-benchmark
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: test-size
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: code
🔇 Additional comments (3)
packages/nuxt/src/compiler/plugins/keyed-functions.ts (3)

1-30: LGTM! Imports and type definitions are well-structured.

The imports use appropriate libraries (unplugin, magic-string, ohash, etc.) and the KeyedFunctionsOptions interface is clearly defined with proper TypeScript typing.


43-43: Static analysis ReDoS warnings are false positives.

The static analysis tool flagged RegExp construction from variables at lines 43, 102, and 227. However, these are safe because:

  1. All inputs are sanitised using escapeRE before being used in the pattern
  2. The sources are controlled (plugin configuration and function names from codebase analysis, not external user input)
  3. These RegExps are created once during plugin initialisation or per-file transform, not in performance-critical loops

No changes needed, but documenting for awareness.

Also applies to: 102-102, 227-227


52-408: Overall implementation is solid with one critical fix needed.

The plugin demonstrates good architectural choices:

  • Proper use of unplugin for cross-bundler compatibility
  • AST-based analysis with oxc-walker and scope tracking
  • MagicString for efficient code transformation with sourcemaps
  • Comprehensive import/export handling (direct, namespace, auto-imports, defaults)
  • Duplicate injection prevention using marker comments

However, the critical fallback issue for entries without source must be addressed before merge.

…/ast-keyed-functions

# Conflicts:
#	packages/nuxt/src/core/nuxt.ts
@huang-julien huang-julien self-requested a review October 19, 2025 09:20
@danielroe danielroe mentioned this pull request Oct 23, 2025
@OrbisK
Copy link
Member

OrbisK commented Nov 10, 2025

I dont think I can help much with the review, but if necessary, I could test it in 1 or 2 projects to check that backward compatibility is ensured.

@cernymatej
Copy link
Member Author

@OrbisK that would be great! 🙏 feel free to reach out on discord if you find anything I should investigate

@OrbisK
Copy link
Member

OrbisK commented Nov 11, 2025

I have tested some projects that use keyedComposables. Looks good so far. I did not encounter any issues.

@danielroe danielroe added this to the 4.3 milestone Dec 16, 2025
@cernymatej
Copy link
Member Author

Has something changed? I think the tests were all passing.

@danielroe
Copy link
Member

yes, i think it's a change i made (exsolve instead of async resolve). i just need to add in resolveAlias in there....

Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

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

thank you ❤️

@danielroe danielroe merged commit d5a793d into nuxt:main Dec 23, 2025
97 of 100 checks passed
@cernymatej cernymatej deleted the refactor/ast-keyed-functions branch December 23, 2025 13:51
@github-actions github-actions bot mentioned this pull request Dec 23, 2025
@cernymatej cernymatej restored the refactor/ast-keyed-functions branch December 23, 2025 17:59
@cernymatej cernymatej deleted the refactor/ast-keyed-functions branch December 23, 2025 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

keyedComposables doesn't inject the key when a composable is imported in a module

3 participants