fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ - #23653

Merged
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged

fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\#23653
s1gr1d merged 14 commits into
getsentry:developfrom
halillusion:fix/nuxt-windows-file-url

Conversation

@halillusion

Copy link
Copy Markdown
Contributor
  • tests added if needed
  • yarn lint passes
  • no related issue, auto-link is fine

Windows fix for Nuxt dev on Node 24.

On Windows npm run dev was failing with ERR_UNSUPPORTED_ESM_URL_SCHEME / Received protocol 'c:' because .nuxt/dev/index.mjs generated import 'C:\...' instead of file:///C:/.... Same isExternal check in rollup.module.config.mjs treated C:\ as external and broke the build.

Changed addServerConfig.ts (and the solidstart copy) to emit pathToFileURL(...).href and fixed isExternal to use isAbsolute() so Windows absolute paths work. Also handles POSIX fine.

Verified with yarn nx run @sentry/nuxt:build:dev -> now passes, and pathToFileURL gives file:///C:/... on Windows.

Closes #XXXX

@halillusion
halillusion requested a review from a team as a code ownerAugust 26, 2026 12:36
@halillusion
halillusion requested review from mydea and s1gr1d and removed request for a teamAugust 26, 2026 12:36
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
On Windows fileURLToPath returns backslash paths (C:\...), so includes('/sentry.server.config') never matched. The config was not marked moduleSideEffects:true and could be tree-shaken, disabling Sentry server init. Normalized paths to forward slashes before the check and keep file:// emission for Node ESM while mapping file:// back to filesystem paths for Rollup.
Fixes handling for both packages/nuxt and packages/solidstart wrapEntry plugins.
// Anything that isn't a relative path is provided by the consuming app or Node at runtime
// (this covers `@sentry/*`, `nuxt/app`, `#imports`, node builtins), so it stays external.
const isExternal = id => !id.startsWith('.') && !id.startsWith('/') && !id.startsWith('\0');
const isExternal = id => !id.startsWith('.') && !isAbsolute(id) && !id.startsWith('\0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch!

return {
name: 'sentry-wrap-entry-with-dynamic-import',
async resolveId(source, importer, options) {
if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could probably just do this:

if (path.basename(source).startsWith(SERVER_CONFIG_FILENAME)) {

This does not include the forward slash anymore.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts Outdated
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @s1gr1d — Please review this PR when you get a chance!

@s1gr1ds1gr1d left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some general comments:

  • there are no new tests. Would be great to add unit tests for the new branches in the rollup plugins
  • formatting fails: run yarn format in the root

Comment on lines +191 to +198
let normalizedSource = source;
if (source.startsWith('file://')) {
try {
normalizedSource = fileURLToPath(source);
} catch {
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part is very much the same from the part below where source.startsWith( is checked again.

You could write a util function, e.g.

/** * `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows * paths (`ERR_UNSUPPORTED_ESM_URL_SCHEME`), but Rollup's resolver only understands * filesystem paths. Returns `undefined` for a malformed `file://` URL. */exportfunctiontoResolvablePath(source: string): {path: string;wasFileUrl: boolean}|undefined{if(!source.startsWith('file://')){return{path: source,wasFileUrl: false};}try{return{path: fileURLToPath(source),wasFileUrl: true};}catch{returnundefined;}}

Comment on lines +234 to +238
if (source.startsWith('file://')) {
const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false });
if (resolved) return resolved;
return { id: normalizedSource };
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the helper util I mentioned above, you could do this here:

if(wasFileUrl){// other code}

On Windows, Node's ESM loader rejects bare absolute paths (C:\...) with ERR_UNSUPPORTED_ESM_URL_SCHEME (protocol 'c:').
Emit file:// URLs from wrapEntryWithDynamicImport load hook for the Sentry server config, wrapped entry point, and serverless re-exports. In resolveId, normalize incoming file:// URLs back to filesystem paths via toResolvablePath() and forward them with isEntry: false to prevent double-wrapping.
Applied symmetrically to @sentry/nuxt and @sentry/solidstart, with unit tests covering URL normalization and entry resolution.
Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Please merge in the changes from develop and resolve the conflicts.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28367c0. Configure here.

Comment threadpackages/nuxt/src/vite/addServerConfig.ts
@s1gr1d

Copy link
Copy Markdown
Member

Some tests are still failing:

 ❯ test/vite/addServerConfig.test.ts (18 tests | 2 failed) 38ms
✓ toResolvablePath > passes through non-file specifiers 5ms
✓ toResolvablePath > converts file:// URLs to filesystem paths 0ms
× toResolvablePath > returns undefined for malformed file:// URLs 15ms
→ expected { path: '/', wasFileUrl: true } to be undefined
✓ wrapEntryWithDynamicImport > emits file:// URLs from load() so Node resolves them on Windows 1ms
✓ wrapEntryWithDynamicImport > uses file:// URLs for re-exported functions 1ms
✓ wrapEntryWithDynamicImport > resolves a file:// config URL to a filesystem path with moduleSideEffects 1ms
✓ wrapEntryWithDynamicImport > resolves a plain config path without converting it 0ms
✓ wrapEntryWithDynamicImport > does not mark backup or test config files as the Sentry server config 2ms
✓ wrapEntryWithDynamicImport > resolves file:// entry specifiers without re-entering the entry branch 1ms
× wrapEntryWithDynamicImport > returns null for malformed file:// URLs 2ms
→ expected { id: '/' } to be null

@halillusion

Copy link
Copy Markdown
ContributorAuthor

Synced with develop and addressed the latest feedback:

  1. POSIX file:// handling: Fixed toResolvablePath returning { path: '/', wasFileUrl: true } on Linux for malformed file:// URLs.
  2. Resolver slash prefix: Removed leading slashes from createResolver.resolve() in addServerConfig.ts to ensure clean Windows absolute path resolution.
  3. Cross-platform tests: All unit tests in @sentry/nuxt (17 files, 331 tests) now pass cleanly.

Comment threadCLAUDE.md
@s1gr1d

Copy link
Copy Markdown
Member

I merged develop into your branch to see if CI is green - it works now. Just one little comment as there's a change in the claude file.

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea — Please review this PR when you get a chance!

@s1gr1d
s1gr1d merged commit d97a57b into getsentry:developSep 3, 2026
521 of 523 checks passed
Sign up for freeto 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.

2 participants

@halillusion@s1gr1d