Skip to content

fix: escape virtual module import paths with apostrophes#2143

Open
Arktomson wants to merge 2 commits intowxt-dev:mainfrom
Arktomson:fix-1697-virtual-module-quote
Open

fix: escape virtual module import paths with apostrophes#2143
Arktomson wants to merge 2 commits intowxt-dev:mainfrom
Arktomson:fix-1697-virtual-module-quote

Conversation

@Arktomson
Copy link
Contributor

@Arktomson Arktomson commented Feb 21, 2026

Overview

When a user's project path contains an apostrophe (e.g. /tmp/foo'bar/), building the extension fails because virtual module templates use a simple string replacement that doesn't account for quote characters in file paths. The path gets inserted directly into an import statement like import definition from '/tmp/foo'bar/background.ts', producing invalid JavaScript.

This fix replaces the naive .replace() with a quote-aware approach: it matches the full quoted import specifier (including surrounding quotes) and replaces it with JSON.stringify(inputPath), which properly escapes any special characters in the path.

A unit test is added to verify both single-quoted and double-quoted template variants handle paths with apostrophes correctly.

Manual Testing

  1. Create a project directory with an apostrophe in the path, e.g. mkdir -p "/tmp/foo'bar" and scaffold a WXT project inside it
  2. Run wxt build — previously this would fail with a syntax error
  3. Verify the build now completes successfully

Related Issue

This PR closes #1697

@netlify
Copy link

netlify bot commented Feb 21, 2026

Deploy Preview for creative-fairy-df92c4 failed.

Name Link
🔨 Latest commit 7e92908
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/6999d681e3c9f700080bbb0c

Copy link
Member

@aklinker1 aklinker1 left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. A few small changes to the tests, then we can merge!

'\0virtual:wxt-background-entrypoint?' + inputPath,
);

expect(code).toBe(`import definition from ${JSON.stringify(inputPath)};`);
Copy link
Member

Choose a reason for hiding this comment

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

We should hardcode the expected path here so it's clear what's being escaped and so the test doesn't rely on the same API as the runtime code.

Is this correct? Or should there be one more \?

Suggested change
expect(code).toBe(`import definition from ${JSON.stringify(inputPath)};`);
expect(code).toBe(`import definition from "/tmp/foo\'bar/background.ts";`);

When I run it in the browser, I just get "/tmp/foo'bar/background.ts" without any escape characters...

> console.log(JSON.stringify(`/tmp/foo'bar/background.ts`))
"/tmp/foo'bar/background.ts"

Copy link
Member

@aklinker1 aklinker1 Feb 21, 2026

Choose a reason for hiding this comment

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

Oh wait 🤦 I see, the problem is just that the string was defined with single quotes, so having another one in it is invalid:

'/tmp/foo'bar/background.ts'

Can you also add an expected test for when a project path has a double quote " in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

`import definition from 'virtual:user-background-entrypoint';`,
`import definition from "virtual:user-background-entrypoint";`,
])(
'should escape input paths when template contains %s',
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
'should escape input paths when template contains %s',
'should escape input paths with apostrophes when encountering: %s',

Mention that we're testing apostrophes specifically in this test.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 21, 2026

Open in StackBlitz

@wxt-dev/analytics

npm i https://pkg.pr.new/@wxt-dev/analytics@2143

@wxt-dev/auto-icons

npm i https://pkg.pr.new/@wxt-dev/auto-icons@2143

@wxt-dev/browser

npm i https://pkg.pr.new/@wxt-dev/browser@2143

@wxt-dev/i18n

npm i https://pkg.pr.new/@wxt-dev/i18n@2143

@wxt-dev/module-react

npm i https://pkg.pr.new/@wxt-dev/module-react@2143

@wxt-dev/module-solid

npm i https://pkg.pr.new/@wxt-dev/module-solid@2143

@wxt-dev/module-svelte

npm i https://pkg.pr.new/@wxt-dev/module-svelte@2143

@wxt-dev/module-vue

npm i https://pkg.pr.new/@wxt-dev/module-vue@2143

@wxt-dev/runner

npm i https://pkg.pr.new/@wxt-dev/runner@2143

@wxt-dev/storage

npm i https://pkg.pr.new/@wxt-dev/storage@2143

@wxt-dev/unocss

npm i https://pkg.pr.new/@wxt-dev/unocss@2143

@wxt-dev/webextension-polyfill

npm i https://pkg.pr.new/@wxt-dev/webextension-polyfill@2143

wxt

npm i https://pkg.pr.new/wxt@2143

commit: 7e92908

@codecov
Copy link

codecov bot commented Feb 21, 2026

Codecov Report

❌ Patch coverage is 83.33333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 75.98%. Comparing base (430a3ac) to head (7e92908).

Files with missing lines Patch % Lines
...ore/builders/vite/plugins/resolveVirtualModules.ts 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2143      +/-   ##
==========================================
- Coverage   76.00%   75.98%   -0.03%     
==========================================
  Files         113      113              
  Lines        3047     3052       +5     
  Branches      683      684       +1     
==========================================
+ Hits         2316     2319       +3     
- Misses        645      647       +2     
  Partials       86       86              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aklinker1
Copy link
Member

I did a bit more research here. Should we use pathToFileURL(absolutePath).href from node:url instead of custom logic? I imagine there are more edge cases that the built-in function handles.

@Arktomson
Copy link
Contributor Author

Thanks for the PR. A few small changes to the tests, then we can merge!

ok, let me continue editing.

@aklinker1
Copy link
Member

@Arktomson could you also look at #2146? I think providing this cleanup logic could be a good util for WXT to export as well. But it needs to handle more special characters in file paths to be generic enough.

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.

Project name with ' fails on wxt command

2 participants