Skip to content

feat: support arbitrary entry file name#57

Merged
ktsn merged 3 commits intomainfrom
retain-filename
Feb 23, 2026
Merged

feat: support arbitrary entry file name#57
ktsn merged 3 commits intomainfrom
retain-filename

Conversation

@ktsn
Copy link
Owner

@ktsn ktsn commented Feb 23, 2026

Summary

  • Replace named exports with a default export object map in the server virtual entry, removing the pathToExportName encoding. This allows component file names with non-ASCII characters (e.g., Japanese) or special characters to work correctly.
  • Remove the now-unused pathToExportName function from src/build/paths.ts.
  • Add a test case with a non-ASCII file name (テスト.vue).

Test plan

  • Existing dev and prod snapshot tests updated and passing
  • New "Non-ascii file name" test case added for both dev and prod

Summary by CodeRabbit

  • New Features
    • Added support for Vue components with non-ASCII filenames.

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

@ktsn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 414fece and 7767c84.

📒 Files selected for processing (1)
  • src/build/generate.ts
📝 Walkthrough

Walkthrough

This PR refactors the server virtual entry module from exporting named components to exporting a single default object with component mappings. The pathToExportName function is removed, and component lookup is updated to index into the default export object using the component path as the key. A test fixture with a non-ASCII filename is added.

Changes

Cohort / File(s) Summary
Build Generation Refactor
src/build/generate.ts, src/build/paths.ts
Changed server virtual entry from named per-id re-exports to a single default export object. Removed the pathToExportName function and replaced its usage with path-based keys in the default export mapping.
Server-side Render Updates
src/server/render.ts, src/server/render.test.ts
Updated component lookup to index into the default export object via m.default[componentPath] instead of using the sanitized export name. Test fixtures updated to reflect new export format.
Test Fixtures
test/fixtures/pages/テスト.vue, test/utils.ts
Added a new test fixture component with non-ASCII filename and corresponding test case to validate non-Latin filename handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through export names,
Finds them too long, too hard to tame!
One object now, with keys so clean,
The tidiest bundle we've ever seen! ✨
Default exports make the path quite clear—
No more named chaos, just cheer, cheer, cheer! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: refactoring from named exports with path-encoded names to a default export object map, which enables support for arbitrary (including non-ASCII) entry file names without encoding.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch retain-filename

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/build/generate.ts (1)

1-1: ⚠️ Potential issue | 🟡 Minor

CI failure: oxfmt formatting check is failing on this file.

Run oxfmt --write to fix the formatting issues flagged in the pipeline.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/build/generate.ts` at line 1, CI indicates formatting errors in this file
(the import statement "import path from 'node:path'" is part of the file); run
the formatter to fix it by executing the project formatting command (e.g., run
`oxfmt --write` from the repo root) or apply the project's oxfmt formatting
rules to the file and commit the updated file so the import and surrounding code
conform to the repository's style.
🧹 Nitpick comments (1)
src/build/generate.ts (1)

31-32: Unescaped key in generated JS string literal.

If the relative path contains characters that are special inside a JS single-quoted string (e.g., ' or \), the generated code will be syntactically invalid. While unlikely for typical .vue filenames, this is a latent injection/syntax risk in code generation. Consider escaping the key:

const escaped = key.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
return `  '${escaped}': _${i}`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/build/generate.ts` around lines 31 - 32, The generated object key built
from path.relative(entryDir, id) (variable key) is inserted raw into a
single-quoted JS string, which breaks if the filename contains single quotes or
backslashes; before returning the template string in the generator (the line
that currently returns `  '${key}': _${i}`), escape backslashes and single
quotes in key (e.g., replace \ with \\ and ' with \') and use that escaped value
in the returned string so the output is always a valid single-quoted JS literal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/build/generate.ts`:
- Around line 25-36: The generated object keys use path.relative(entryDir, id)
which yields OS-native separators (e.g. backslashes on Windows); wrap that
result with normalizePath so keys are always POSIX-style. Update the key
computation in the entries mapping (the const key used to build entries from
componentIds) to call normalizePath on the path.relative(...).replace(/\.vue$/,
'') result so consumers can reliably call m.default[componentPath];
normalizePath is already imported and should be used here.

---

Outside diff comments:
In `@src/build/generate.ts`:
- Line 1: CI indicates formatting errors in this file (the import statement
"import path from 'node:path'" is part of the file); run the formatter to fix it
by executing the project formatting command (e.g., run `oxfmt --write` from the
repo root) or apply the project's oxfmt formatting rules to the file and commit
the updated file so the import and surrounding code conform to the repository's
style.

---

Nitpick comments:
In `@src/build/generate.ts`:
- Around line 31-32: The generated object key built from path.relative(entryDir,
id) (variable key) is inserted raw into a single-quoted JS string, which breaks
if the filename contains single quotes or backslashes; before returning the
template string in the generator (the line that currently returns `  '${key}':
_${i}`), escape backslashes and single quotes in key (e.g., replace \ with \\
and ' with \') and use that escaped value in the returned string so the output
is always a valid single-quoted JS literal.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c438cac and 414fece.

⛔ Files ignored due to path filters (2)
  • test/__snapshots__/dev.test.ts.snap is excluded by !**/*.snap
  • test/__snapshots__/prod.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • src/build/generate.ts
  • src/build/paths.ts
  • src/server/render.test.ts
  • src/server/render.ts
  • test/fixtures/pages/テスト.vue
  • test/utils.ts
💤 Files with no reviewable changes (1)
  • src/build/paths.ts

Comment on lines 25 to 36
const imports = componentIds
.map((id, i) => `import _${i} from '${id}'`)
.join('\n')

const entries = componentIds
.map((id, i) => {
const key = path.relative(entryDir, id).replace(/\.vue$/, '')
return ` '${key}': _${i}`
})
.join(',\n')

return `${imports}\nexport default {\n${entries}\n}`
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the full generate.ts file to understand context
head -50 src/build/generate.ts

Repository: ktsn/visle

Length of output: 1544


🏁 Script executed:

# Check render.ts to see how the keys are consumed at line 55
sed -n '50,60p' src/build/render.ts

Repository: ktsn/visle

Length of output: 116


🏁 Script executed:

# Find where componentIds come from and how they're formatted
rg -n 'componentIds' --type ts -B3 -A3

Repository: ktsn/visle

Length of output: 1234


🏁 Script executed:

# Check if normalizePath is actually imported in generate.ts
rg -n 'normalizePath|import.*from.*vite' src/build/generate.ts

Repository: ktsn/visle

Length of output: 437


🏁 Script executed:

# Check for oxfmt configuration and any formatting issues
find . -name '.oxfmtrc*' -o -name 'oxfmt.config*' 2>/dev/null | head -10

Repository: ktsn/visle

Length of output: 69


🏁 Script executed:

# Check if oxfmt is mentioned in package.json or CI config
rg -i 'oxfmt' --type json --type yaml --type toml

Repository: ktsn/visle

Length of output: 3736


🏁 Script executed:

# Find where generateServerVirtualEntryCode is called
rg 'generateServerVirtualEntryCode' --type ts

Repository: ktsn/visle

Length of output: 371


🏁 Script executed:

# Check if there's a git diff or status for generate.ts
git diff src/build/generate.ts 2>/dev/null || echo "No git diff available"
git status src/build/generate.ts 2>/dev/null || echo "No git status available"

Repository: ktsn/visle

Length of output: 119


🏁 Script executed:

# Check recent commits mentioning generate.ts or oxfmt
git log --oneline --all -20 2>/dev/null | head -20

Repository: ktsn/visle

Length of output: 1216


🏁 Script executed:

# Verify oxfmt would flag this file - check the actual formatting
cat .oxfmtrc.json

Repository: ktsn/visle

Length of output: 231


🏁 Script executed:

# Look for any CI/build log that shows oxfmt failure
find . -name '*.log' -o -name '.github' -type d 2>/dev/null | head -5

Repository: ktsn/visle

Length of output: 63


🏁 Script executed:

# Search for where componentIds come from in the codebase
rg 'componentIds.*=' --type ts -B2 -A2

Repository: ktsn/visle

Length of output: 36


🏁 Script executed:

# Look at where resolveServerComponentIds is called
rg 'resolveServerComponentIds' --type ts -B5 -A5

Repository: ktsn/visle

Length of output: 2137


🏁 Script executed:

# Check the recent commit that mentions pathToExportName fix
git show 45f123c --stat 2>/dev/null | head -40

Repository: ktsn/visle

Length of output: 991


🏁 Script executed:

# Look at the virtual-file.ts context around generateServerVirtualEntryCode call
rg -B10 -A5 'generateServerVirtualEntryCode\(entryRoot' src/build/plugins/virtual-file.ts

Repository: ktsn/visle

Length of output: 450


🏁 Script executed:

# Check what componentIds format is (look at resolveServerComponentIds implementation)
rg 'function resolveServerComponentIds|const resolveServerComponentIds' --type ts -A15

Repository: ktsn/visle

Length of output: 887


🏁 Script executed:

# Check resolvePattern to understand the format of componentIds
rg 'function resolvePattern|const resolvePattern' --type ts -A10

Repository: ktsn/visle

Length of output: 546


🏁 Script executed:

# Look for recent PR or branch info
git branch -a 2>/dev/null || echo "No branches available"

Repository: ktsn/visle

Length of output: 151


🏁 Script executed:

# Check if there's a specific commit or branch with the changes from the review
git log --all --oneline --grep="generate" 2>/dev/null | head -10

Repository: ktsn/visle

Length of output: 236


🏁 Script executed:

# Look at the full paths.ts to understand resolvePattern
cat src/build/paths.ts | head -80

Repository: ktsn/visle

Length of output: 2173


🏁 Script executed:

# Check commit 45f123c to see what pathToExportName was doing before
git show 45f123c src/build/generate.ts | head -100

Repository: ktsn/visle

Length of output: 1380


🏁 Script executed:

# Check if there's any usage of the key elsewhere in the codebase
rg 'default\[' --type ts -B2 -A2

Repository: ktsn/visle

Length of output: 297


🏁 Script executed:

# Verify if componentIds keys are used anywhere
rg 'entryDefault\[|m\.default\[' --type ts -B3 -A3

Repository: ktsn/visle

Length of output: 418


🏁 Script executed:

# Check the most recent commit to see if path issue was already fixed
git show 414fece --stat | head -30

Repository: ktsn/visle

Length of output: 1709


🏁 Script executed:

# Check the current state of generate.ts in the latest commit
git show 414fece:src/build/generate.ts 2>/dev/null | head -50

Repository: ktsn/visle

Length of output: 1544


🏁 Script executed:

# See what changed between commit 45f123c and 414fece
git log --oneline 45f123c..414fece 2>/dev/null

Repository: ktsn/visle

Length of output: 101


🏁 Script executed:

# Verify that the current generate.ts still has the issue
grep -n "path.relative.*entryDir" src/build/generate.ts

Repository: ktsn/visle

Length of output: 124


🏁 Script executed:

# Verify how the key is used - check if componentPath from user matches the key format
rg 'componentPath' src/server/render.ts -B5 -A5

Repository: ktsn/visle

Length of output: 1283


🏁 Script executed:

# Check if there are tests that would catch this Windows issue
fd 'test|spec' --type f -e ts | grep -E '(generate|render)' | head -10

Repository: ktsn/visle

Length of output: 79


🏁 Script executed:

# Check the render test file
cat src/server/render.test.ts

Repository: ktsn/visle

Length of output: 5152


🏁 Script executed:

# Also check if there are integration tests
find . -name '*.test.ts' -o -name '*.spec.ts' | head -20

Repository: ktsn/visle

Length of output: 206


🏁 Script executed:

# Check the full render.ts file to understand the complete flow
cat src/server/render.ts

Repository: ktsn/visle

Length of output: 2608


Apply normalizePath to ensure cross-platform compatibility with object keys.

path.relative(entryDir, id) produces OS-native separators. On Windows this yields keys like 'nested\\index', but consumers expect POSIX-style keys like 'nested/index' when calling render('nested/index'), causing a lookup miss in m.default[componentPath].

Since normalizePath is already imported from Vite and used elsewhere in this file for the same purpose, apply it to the key:

Proposed fix
   const entries = componentIds
     .map((id, i) => {
-      const key = path.relative(entryDir, id).replace(/\.vue$/, '')
+      const key = normalizePath(path.relative(entryDir, id)).replace(/\.vue$/, '')
       return `  '${key}': _${i}`
     })
     .join(',\n')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const imports = componentIds
.map((id, i) => `import _${i} from '${id}'`)
.join('\n')
const entries = componentIds
.map((id, i) => {
const key = path.relative(entryDir, id).replace(/\.vue$/, '')
return ` '${key}': _${i}`
})
.join(',\n')
return `${imports}\nexport default {\n${entries}\n}`
const imports = componentIds
.map((id, i) => `import _${i} from '${id}'`)
.join('\n')
const entries = componentIds
.map((id, i) => {
const key = normalizePath(path.relative(entryDir, id)).replace(/\.vue$/, '')
return ` '${key}': _${i}`
})
.join(',\n')
return `${imports}\nexport default {\n${entries}\n}`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/build/generate.ts` around lines 25 - 36, The generated object keys use
path.relative(entryDir, id) which yields OS-native separators (e.g. backslashes
on Windows); wrap that result with normalizePath so keys are always POSIX-style.
Update the key computation in the entries mapping (the const key used to build
entries from componentIds) to call normalizePath on the
path.relative(...).replace(/\.vue$/, '') result so consumers can reliably call
m.default[componentPath]; normalizePath is already imported and should be used
here.

Wrap path.relative result with normalizePath so generated object keys
use forward slashes on all platforms.
@ktsn ktsn enabled auto-merge (squash) February 23, 2026 13:29
@ktsn ktsn merged commit 48f6310 into main Feb 23, 2026
4 checks passed
@ktsn ktsn deleted the retain-filename branch February 23, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant