-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(nuxt): Windows file:// for import-in-the-middle hook and isAbsolute for C:\ #23653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
s1gr1d
merged 14 commits into
getsentry:develop
from
halillusion:fix/nuxt-windows-file-url
Sep 3, 2026
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d237ce3
fix(nuxt): Windows file:// for import and isAbsolute for C:"
halillusion 837e9b5
fix: introduce Nitro rollup plugins for Sentry server configuration i…
halillusion f1dbffb
fix(nuxt): normalize Windows paths for server config detection
halillusion e50766e
fix: Windows dev overlay by emitting file:// URLs and fixing s1gr1d r…
halillusion 22cfd7f
fix(nuxt): prevent double-wrapping of file:// specifiers in server en…
halillusion 5cc77ee
fix(nuxt): emit file:// URLs from server entry wrapper for Windows ESM
halillusion 886b767
fix(nuxt): narrow server config detection to exact match and valid ex…
halillusion a0cce99
Merge branch 'develop' into fix/nuxt-windows-file-url
halillusion 28367c0
Merge branch 'develop' into fix/nuxt-windows-file-url
halillusion a67d220
Merge branch 'develop' into fix/nuxt-windows-file-url
halillusion 9ede588
fix(nuxt): handle POSIX file:// URL validation and drop leading slash…
halillusion 68327b9
test(nuxt): make findDefaultSdkInitFile tests cross-platform for Windows
halillusion 1a36f2f
Merge branch 'develop' into fix/nuxt-windows-file-url
s1gr1d e50d826
revert: restore CLAUDE.md symlink without trailing newline
halillusion File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import { existsSync } from 'node:fs'; | ||
| import { basename } from 'node:path'; | ||
| import { pathToFileURL } from 'node:url'; | ||
| import { addTemplate, createResolver } from '@nuxt/kit'; | ||
| import type { Nuxt } from '@nuxt/schema'; | ||
|
|
@@ -20,6 +21,7 @@ import { | |
| SENTRY_WRAPPED_FUNCTIONS, | ||
| SERVER_CONFIG_FILENAME, | ||
| toImportSpecifier, | ||
| toResolvablePath, | ||
| } from './utils'; | ||
|
|
||
| /** Path of the generated dev-mode config file, relative to the Nuxt build directory. */ | ||
|
|
@@ -31,7 +33,7 @@ export const DEV_SERVER_CONFIG_PATH = `dev/${SERVER_CONFIG_FILENAME}.mjs`; | |
| * In dev-mode, Nitro v3 has no server bundle to emit into, so Node loads the server config file as it is written. | ||
| */ | ||
| export function addDevServerConfigFile(nuxt: Nuxt, serverConfigFile: string): void { | ||
| const configPath = createResolver(nuxt.options.rootDir).resolve(`/${serverConfigFile}`); | ||
| const configPath = createResolver(nuxt.options.rootDir).resolve(serverConfigFile); | ||
| const importSpecifier = toImportSpecifier( | ||
| nuxt.options.rootDir, | ||
| path.join(nuxt.options.buildDir, DEV_SERVER_CONFIG_PATH), | ||
|
|
@@ -59,6 +61,15 @@ export function addDevServerConfigFile(nuxt: Nuxt, serverConfigFile: string): vo | |
| ].join('\n'), | ||
| }); | ||
| } | ||
| const CONFIG_EXTENSIONS = ['.ts', '.js', '.mjs', '.cjs', '.mts', '.cts']; | ||
|
|
||
| function isServerConfigFile(sourcePath: string, resolvedPath: string): boolean { | ||
| if (sourcePath === resolvedPath) { | ||
| return true; | ||
| } | ||
| const name = basename(sourcePath); | ||
| return name === SERVER_CONFIG_FILENAME || CONFIG_EXTENSIONS.some(ext => name === `${SERVER_CONFIG_FILENAME}${ext}`); | ||
| } | ||
|
|
||
| /** | ||
| * Adds the `sentry.server.config.ts` file as `sentry.server.config.mjs` to the `.output` directory to be able to reference this file in the node --import option. | ||
|
|
@@ -157,7 +168,7 @@ export function addDynamicImportEntryFileWrapper( | |
|
|
||
| nitro.options.rollupConfig.plugins.push( | ||
| wrapEntryWithDynamicImport({ | ||
| resolvedSentryConfigPath: createResolver(nitro.options.rootDir).resolve(`/${serverConfigFile}`), | ||
| resolvedSentryConfigPath: createResolver(nitro.options.rootDir).resolve(serverConfigFile), | ||
| experimental_entrypointWrappedFunctions: moduleOptions.experimental_entrypointWrappedFunctions, | ||
| }), | ||
| ); | ||
|
|
@@ -173,7 +184,7 @@ function injectServerConfigPlugin(nitro: Nitro, serverConfigFile: string, isDebu | |
| name: 'rollup-plugin-inject-sentry-server-config', | ||
|
|
||
| buildStart() { | ||
| const configPath = createResolver(nitro.options.rootDir).resolve(`/${serverConfigFile}`); | ||
| const configPath = createResolver(nitro.options.rootDir).resolve(serverConfigFile); | ||
|
|
||
| if (!existsSync(configPath)) { | ||
| if (isDebug) { | ||
|
|
@@ -193,7 +204,7 @@ function injectServerConfigPlugin(nitro: Nitro, serverConfigFile: string, isDebu | |
| resolveId(source) { | ||
| if (source.startsWith(filePrefix)) { | ||
| const originalFilePath = source.replace(filePrefix, ''); | ||
| const configPath = createResolver(nitro.options.rootDir).resolve(`/${originalFilePath}`); | ||
| const configPath = createResolver(nitro.options.rootDir).resolve(originalFilePath); | ||
|
|
||
| return { id: configPath }; | ||
| } | ||
|
|
@@ -206,8 +217,10 @@ function injectServerConfigPlugin(nitro: Nitro, serverConfigFile: string, isDebu | |
| * A Rollup plugin which wraps the server entry with a dynamic `import()`. This makes it possible to initialize Sentry first | ||
| * by using a regular `import` and load the server after that. | ||
| * This also works with serverless `handler` functions, as it re-exports the `handler`. | ||
| * | ||
| * Only exported for testing. | ||
| */ | ||
| function wrapEntryWithDynamicImport({ | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| export function wrapEntryWithDynamicImport({ | ||
| resolvedSentryConfigPath, | ||
| experimental_entrypointWrappedFunctions, | ||
| debug, | ||
|
|
@@ -225,12 +238,24 @@ function wrapEntryWithDynamicImport({ | |
| return { | ||
| name: 'sentry-wrap-entry-with-dynamic-import', | ||
| async resolveId(source, importer, options) { | ||
| if (source.includes(`/${SERVER_CONFIG_FILENAME}`)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could probably just do this: This does not include the forward slash anymore. |
||
| return { id: source, moduleSideEffects: true }; | ||
| // `load()` emits `file://` specifiers because Node's ESM loader rejects bare Windows paths, | ||
| // but Rollup's resolver only understands filesystem paths. | ||
| const resolvable = toResolvablePath(source); | ||
| if (!resolvable) { | ||
| return null; | ||
| } | ||
| const { path: normalizedSource, wasFileUrl } = resolvable; | ||
|
|
||
| if (options.isEntry && source.includes('.mjs') && !source.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { | ||
| const resolution = await this.resolve(source, importer, options); | ||
| if (isServerConfigFile(normalizedSource, resolvedSentryConfigPath)) { | ||
| return { id: normalizedSource, moduleSideEffects: true }; | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| if ( | ||
| options.isEntry && | ||
| normalizedSource.includes('.mjs') && | ||
| !normalizedSource.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`) | ||
| ) { | ||
| const resolution = await this.resolve(normalizedSource, importer, options); | ||
|
|
||
| // If it cannot be resolved or is external, just return it so that Rollup can display an error | ||
| if (!resolution || resolution?.external) return resolution; | ||
|
halillusion marked this conversation as resolved.
|
||
|
|
@@ -254,24 +279,36 @@ function wrapEntryWithDynamicImport({ | |
| ) | ||
| .concat(QUERY_END_INDICATOR)}`; | ||
| } | ||
|
|
||
| // Pass isEntry:false to avoid re-entering the isEntry branch and double-wrapping | ||
| // (normalizedSource strips the SENTRY_WRAPPED_ENTRY query suffix). | ||
| if (wasFileUrl) { | ||
| const resolved = await this.resolve(normalizedSource, importer, { ...options, isEntry: false }); | ||
| if (resolved) return resolved; | ||
| return { id: normalizedSource }; | ||
| } | ||
|
|
||
| return null; | ||
| }, | ||
| load(id: string) { | ||
| if (id.includes(`.mjs${SENTRY_WRAPPED_ENTRY}`)) { | ||
| const entryId = removeSentryQueryFromPath(id).slice(resolutionIdPrefix.length); | ||
| const entryIdUrl = pathToFileURL(entryId).href; | ||
| const configUrl = pathToFileURL(resolvedSentryConfigPath).href; | ||
|
|
||
| // Use entryIdUrl so Node's runtime ESM loader receives file:// on Windows; Rollup normalizes it in resolveId. | ||
| // Mostly useful for serverless `handler` functions | ||
| const reExportedFunctions = | ||
| id.includes(SENTRY_WRAPPED_FUNCTIONS) || id.includes(SENTRY_REEXPORTED_FUNCTIONS) | ||
| ? constructFunctionReExport(id, entryId) | ||
| ? constructFunctionReExport(id, entryIdUrl) | ||
|
sentry[bot] marked this conversation as resolved.
|
||
| : ''; | ||
|
|
||
| return ( | ||
| // Regular `import` of the Sentry config | ||
| `import ${JSON.stringify(resolvedSentryConfigPath)};\n` + | ||
| `import ${JSON.stringify(configUrl)};\n` + | ||
| // Dynamic `import()` for the previous, actual entry point. | ||
| // `import()` can be used for any code that should be run after the hooks are registered (https://nodejs.org/api/module.html#enabling) | ||
| `import(${JSON.stringify(entryId)});\n` + | ||
| `import(${JSON.stringify(entryIdUrl)});\n` + | ||
|
cursor[bot] marked this conversation as resolved.
halillusion marked this conversation as resolved.
sentry[bot] marked this conversation as resolved.
|
||
| `${reExportedFunctions}\n` | ||
| ); | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!