-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
refactor(nuxt): use AST-aware function key injection #33446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(nuxt): use AST-aware function key injection #33446
Conversation
|
|
@nuxt/kit
@nuxt/nitro-server
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
CodSpeed Performance ReportMerging #33446 will improve performance by 11.73%Comparing Summary
Benchmarks breakdown
Footnotes |
|
@danielroe after giving it some thought, I think it would be much better to make the the previous implementation injected keys when the matching functions were imported from in the current implementation, would it be acceptable to make such change in a minor? |
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds a new runtime entry Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Potential attention areas:
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used📓 Path-based instructions (1)**/*.{ts,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
🧠 Learnings (3)📓 Common learnings📚 Learning: 2024-11-05T15:22:54.759ZApplied to files:
📚 Learning: 2024-12-12T12:36:34.871ZApplied to files:
🧬 Code graph analysis (1)packages/nuxt/src/core/nuxt.ts (3)
🔇 Additional comments (1)
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. Comment |
There was a problem hiding this 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 filespackages/schema/src/types/compiler.ts (1)
6-11: Retainsourceas optional until Nuxt 5, but add a runtime warning for missing entries
All built-in defaults and tests already specifysource, so making it required now would be a breaking change reserved for Nuxt 5. To prevent silent skips, emit a warning (or throw) when anykeyedComposablesentry omitssourceat runtime, with a pointer to the migration guide.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.tspackages/schema/src/types/compiler.tspackages/nuxt/src/components/module.tspackages/nuxt/src/core/nuxt.tspackages/nuxt/src/compiler/plugins/keyed-functions.tspackages/nuxt/src/compiler/parse-utils.tspackages/schema/src/config/build.tspackages/schema/src/types/schema.tspackages/nuxt/build.config.tspackages/nuxt/test/compiler.test.tspackages/nuxt/src/utils.tspackages/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.tspackages/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.tspackages/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
argumentLengtheffectively 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 centralisedKeyedFunctiontype.The import and usage of the
KeyedFunctiontype 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
|
would you take a look at the failing webpack/rspack tests? 🙏 I think it's fine to make there are only a few examples of modules using the old behaviour: https://github.com/search?q=keyedComposables+-is%3Afork&type=code |
There was a problem hiding this 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
📒 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
There was a problem hiding this 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 notesourcerequirementPlease 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).sourceis 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 patternCurrent 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-regexpif needed in this scope.
94-96: Large dynamic include RegExp: cap/optimise prefilterThis 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 errorsYou already extract
scriptand itsindex(codeIndex). Usescript(not fullcode) as the parse target to avoid attempting to parse.vuemarkup in edge toolchains wherepostordering 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
TemplateLiteralas a key (even with expressions). If you want stricter behaviour (only static templates), refine checks to ensurequasis.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 detectionThe plugin guards duplicate warnings behind
import.meta.dev. Instead of stubbing a custom global, ensure the test runs withimport.meta.dev === true(e.g., via Vitest/Vite config) or temporarily monkey‑patchObject.defineProperty(import.meta, 'dev', { value: true })if supported. Currentvi.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
sourcerequired (either inject or deliberately skip).- Add a
.vueSFC 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 testsYou 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
📒 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.tspackages/nuxt/test/keyed-functions.test.tspackages/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 nowBecause
importSourceResolvedis only set for explicit imports or when processing the exportingsourcefile, calls relying solely on auto-imports (no import statement) are skipped. This matches the new “sourcerequired” stance but is a behavioural change.
- Confirm all module
keyedComposablesentries specify a concretesource.- Consider a fallback mode (optional) for
source-less entries if backward compatibility is desired.
…h a RegExp `source`
|
@danielroe the webpack tests failed because, for some reason, certain files were being transformed more than once. This has been resolved by adding a I've also added (hopefully full) backward compatibility for cases where The RegExp support has now been removed on the type-level, and |
There was a problem hiding this 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 callsimport { useFoo } from '~/composables/foo',sourceis the resolved path while the metadata lives under the empty-string bucket, sogetFunctionMetaByLocalNamereturnsundefinedand 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
📒 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.tspackages/nuxt/test/keyed-functions.test.tspackages/nuxt/src/compiler/plugins/keyed-functions.tspackages/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
There was a problem hiding this 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
ComposableKeysPluginwith a newKeyedFunctionsPluginthat uses AST analysis - Makes the
sourceproperty required inKeyedFunctioninterface 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 |
There was a problem hiding this 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 withoutsource.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 withoutsourceto 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 usingprocess.env.NODE_ENVfor build-time checks.
import.meta.devis Vite-specific and may not be reliably available in all build contexts (Webpack, Rspack). For Node.js build plugins,process.env.NODE_ENVis 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
📒 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
KeyedFunctionsOptionsinterface 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:
- All inputs are sanitised using
escapeREbefore being used in the pattern- The sources are controlled (plugin configuration and function names from codebase analysis, not external user input)
- 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
sourcemust be addressed before merge.
…/ast-keyed-functions # Conflicts: # packages/nuxt/src/core/nuxt.ts
|
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. |
|
@OrbisK that would be great! 🙏 feel free to reach out on discord if you find anything I should investigate |
…or/ast-keyed-functions
|
I have tested some projects that use |
|
Has something changed? I think the tests were all passing. |
|
yes, i think it's a change i made (exsolve instead of async resolve). i just need to add in resolveAlias in there.... |
danielroe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you ❤️
🔗 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)
sourceoption in thekeyedComposablesobject 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, thesourceoption has now been made required.This also fixes various issues with the old implementation, where we would inject keys into incorrect functions:
or wouldn't handle renaming in imports:
wouldn't handle default exports
and more cases for calls in the
sourcefiles...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
source(in this case, it should inject to auto-imported functions)