-
Notifications
You must be signed in to change notification settings - Fork 2.1k
@uppy/vue: support kebab-case props in generated components #6125
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
base: main
Are you sure you want to change the base?
Conversation
Vue convention is to use kebab-case for props in templates (e.g., `:edit-file`), but the generated Vue components weren't handling this correctly. The issue: Vue's `attrs` preserves the casing from templates. When using `:edit-file`, Vue passes `attrs['edit-file']` to the component, but Preact expects camelCase (`editFile`). The fix: Generate Vue components with explicit `props` arrays. When Vue components declare their props, Vue automatically converts kebab-case template usage to camelCase in the `props` object. This is the standard Vue approach - not a hacky conversion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: dda2970 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
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
Copilot reviewed 2 out of 8 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Match the Props type definition: export type ComponentNameProps = { ... } | ||
| const propsMatch = content.match( | ||
| /export\s+type\s+\w+Props\s*=\s*\{([^}]+)\}/s, | ||
| ) | ||
| if (!propsMatch) { | ||
| return [] | ||
| } | ||
|
|
||
| const propsBlock = propsMatch[1] | ||
|
|
Copilot
AI
Jan 13, 2026
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.
The regex pattern for matching Props type definitions uses [^}]+ which will only capture content up to the first closing brace. While this works for the current simple prop types, it will break if a prop has an inline object type with nested braces. Consider using a more robust parsing approach, such as TypeScript's Compiler API or a recursive brace-matching algorithm, to handle nested type definitions.
| // Match the Props type definition: export type ComponentNameProps = { ... } | |
| const propsMatch = content.match( | |
| /export\s+type\s+\w+Props\s*=\s*\{([^}]+)\}/s, | |
| ) | |
| if (!propsMatch) { | |
| return [] | |
| } | |
| const propsBlock = propsMatch[1] | |
| // Match the start of the Props type definition: export type ComponentNameProps = { ... } | |
| const propsStartRegex = /export\s+type\s+\w+Props\s*=\s*\{/m | |
| const propsStartMatch = propsStartRegex.exec(content) | |
| if (!propsStartMatch) { | |
| return [] | |
| } | |
| // Find the full Props block by matching balanced braces, to support nested object types | |
| const propsBlockStart = propsStartMatch.index + propsStartMatch[0].length | |
| let braceDepth = 1 | |
| let i = propsBlockStart | |
| while (i < content.length && braceDepth > 0) { | |
| const ch = content[i] | |
| if (ch === '{') { | |
| braceDepth += 1 | |
| } else if (ch === '}') { | |
| braceDepth -= 1 | |
| } | |
| i += 1 | |
| } | |
| if (braceDepth !== 0) { | |
| // Unbalanced braces; fall back to no props | |
| return [] | |
| } | |
| const propsBlock = content.slice(propsBlockStart, i - 1) |
mifi
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.
i assume you tested this, and since it seems now that this is most likely just a temporary solution, i think we can merge it
Summary
:edit-fileinstead of:editFile)migrate.mjsto parse prop names from TypeScript source files and generate explicitpropsarraysProblem
The generated Vue components didn't work correctly with Vue's standard kebab-case prop convention:
Root Cause
The original Vue template used
attrsto pass props to Preact:When using
:edit-filein a Vue template, Vue passesattrs['edit-file'](kebab-case preserved), but Preact expectseditFile(camelCase).Solution
Generate Vue components with explicit
propsdeclarations:When Vue components declare their props, Vue automatically converts kebab-case template usage to camelCase in the
propsobject. This is standard Vue behavior.Why Simpler Alternatives Don't Work
"Just use
propsinstead ofattrs"Without a
propsdeclaration, Vue doesn't know which attributes are props. Thepropsobject will be empty and everything goes toattrs:"Accept all props dynamically"
Vue doesn't have a "accept all props and convert casing" option. You must explicitly declare which props you expect for Vue to handle the conversion.
"Just document to use camelCase"
This works but violates Vue conventions. Every Vue developer expects
:edit-fileto work.