Skip to content

Commit ef59700

Browse files
authored
Revert "ref(nextjs): Use virtual file for proxying in proxy loader (#5960)" (#6018)
In #5960, we switched from writing temporary files to disk to using virtual files when creating proxy modules in the nextjs SDK's proxy loader. Though it was a good change, it didn't handle certain cases with the potential to crash a user's build process. Specifically, rollup converts the absolute paths in the template's `import * as wrappedFile from <wrapped file>` and `export * from <wrapped file>` into relative paths, but can break them in the process, by doing things like replacing `[` and `]` with `_`. Prior to the switch to virtual files, those relative paths only ever had one segment, and so the code compensating for rollup's weirdness only handles basenames. After the switch, the relative paths sometimes have multiple segments, in a pattern which isn't easy to predict. When that happens, the aforementioned compensation code fails and the broken relative paths aren't fixed. After discussing with @lforst, the author of that PR, we agreed to take a different approach to handling the broken paths. Rather than add a fix on top of that change, I'm choosing to revert it and implement the new approach separately, so that the new work is easier to review and the change is easier to reason about. Otherwise, the fix would include a bunch of undoing of changes, which would muddy the waters. #5960 also contained a few small refactors which don't bear directly on the switch to using virtual files, and reverting allows me to pull those out into a separate PR, again for ease of understanding. Reverts 2d068e9.
1 parent ad64c2b commit ef59700

File tree

7 files changed

+37
-45
lines changed

7 files changed

+37
-45
lines changed

packages/nextjs/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
},
1919
"dependencies": {
2020
"@rollup/plugin-sucrase": "4.0.4",
21-
"@rollup/plugin-virtual": "3.0.0",
2221
"@sentry/core": "7.16.0",
2322
"@sentry/integrations": "7.16.0",
2423
"@sentry/node": "7.16.0",

packages/nextjs/rollup.npm.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default [
3333
// make it so Rollup calms down about the fact that we're combining default and named exports
3434
exports: 'named',
3535
},
36-
external: ['@sentry/nextjs', /__RESOURCE_PATH__.*/],
36+
external: ['@sentry/nextjs', '__RESOURCE_PATH__'],
3737
},
3838
}),
3939
),

packages/nextjs/src/config/loaders/proxyLoader.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default async function proxyLoader(this: LoaderThis<LoaderOptions>, userC
3838
// wrapped file, so that we know that it's already been processed. (Adding this query string is also necessary to
3939
// convince webpack that it's a different file than the one it's in the middle of loading now, so that the originals
4040
// themselves will have a chance to load.)
41-
if (this.resourceQuery.includes('__sentry_wrapped__') || this.resourceQuery.includes('__sentry_external__')) {
41+
if (this.resourceQuery.includes('__sentry_wrapped__')) {
4242
return userCode;
4343
}
4444

@@ -55,29 +55,34 @@ export default async function proxyLoader(this: LoaderThis<LoaderOptions>, userC
5555

5656
// Fill in the path to the file we're wrapping and save the result as a temporary file in the same folder (so that
5757
// relative imports and exports are calculated correctly).
58+
//
59+
// TODO: We're saving the filled-in template to disk, however temporarily, because Rollup expects a path to a code
60+
// file, not code itself. There is a rollup plugin which can fake this (`@rollup/plugin-virtual`) but the virtual file
61+
// seems to be inside of a virtual directory (in other words, one level down from where you'd expect it) and that
62+
// messes up relative imports and exports. Presumably there's a way to make it work, though, and if we can, it would
63+
// be cleaner than having to first write and then delete a temporary file each time we run this loader.
5864
templateCode = templateCode.replace(/__RESOURCE_PATH__/g, this.resourcePath);
65+
const tempFilePath = path.resolve(path.dirname(this.resourcePath), `temp${Math.random()}.js`);
66+
fs.writeFileSync(tempFilePath, templateCode);
5967

6068
// Run the proxy module code through Rollup, in order to split the `export * from '<wrapped file>'` out into
6169
// individual exports (which nextjs seems to require), then delete the tempoary file.
62-
63-
let proxyCode = await rollupize(this.resourcePath, templateCode);
70+
let proxyCode = await rollupize(tempFilePath, this.resourcePath);
71+
fs.unlinkSync(tempFilePath);
6472

6573
if (!proxyCode) {
6674
// We will already have thrown a warning in `rollupize`, so no need to do it again here
6775
return userCode;
6876
}
6977

78+
// Add a query string onto all references to the wrapped file, so that webpack will consider it different from the
79+
// non-query-stringged version (which we're already in the middle of loading as we speak), and load it separately from
80+
// this. When the second load happens this loader will run again, but we'll be able to see the query string and will
81+
// know to immediately return without processing. This avoids an infinite loop.
7082
const resourceFilename = path.basename(this.resourcePath);
71-
72-
// For some reason when using virtual files (via the @rollup/plugin-virtual), rollup will always resolve imports with
73-
// absolute imports to relative imports with `..`.In our case we need`.`, which is why we're replacing for that here.
74-
// Also, we're adding a query string onto all references to the wrapped file, so that webpack will consider it
75-
// different from the non - query - stringged version(which we're already in the middle of loading as we speak), and
76-
// load it separately from this. When the second load happens this loader will run again, but we'll be able to see the
77-
// query string and will know to immediately return without processing. This avoids an infinite loop.
7883
proxyCode = proxyCode.replace(
79-
new RegExp(`'../${escapeStringForRegex(resourceFilename)}'`, 'g'),
80-
`'./${resourceFilename}?__sentry_wrapped__'`,
84+
new RegExp(`/${escapeStringForRegex(resourceFilename)}'`, 'g'),
85+
`/${resourceFilename}?__sentry_wrapped__'`,
8186
);
8287

8388
return proxyCode;

packages/nextjs/src/config/loaders/rollup.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
1+
import type { RollupSucraseOptions } from '@rollup/plugin-sucrase';
12
import sucrase from '@rollup/plugin-sucrase';
2-
import virtual from '@rollup/plugin-virtual';
33
import { logger } from '@sentry/utils';
44
import * as path from 'path';
55
import type { InputOptions as RollupInputOptions, OutputOptions as RollupOutputOptions } from 'rollup';
66
import { rollup } from 'rollup';
77

8-
const SENTRY_PROXY_MODULE_NAME = 'sentry-proxy-module';
9-
10-
const getRollupInputOptions = (userModulePath: string, proxyTemplateCode: string): RollupInputOptions => ({
11-
input: SENTRY_PROXY_MODULE_NAME,
12-
8+
const getRollupInputOptions: (proxyPath: string, resourcePath: string) => RollupInputOptions = (
9+
proxyPath,
10+
resourcePath,
11+
) => ({
12+
input: proxyPath,
1313
plugins: [
14-
virtual({
15-
[SENTRY_PROXY_MODULE_NAME]: proxyTemplateCode,
16-
}),
14+
// For some reason, even though everything in `RollupSucraseOptions` besides `transforms` is supposed to be
15+
// optional, TS complains that there are a bunch of missing properties (hence the typecast). Similar to
16+
// https://github.com/microsoft/TypeScript/issues/20722, though that's been fixed. (In this case it's an interface
17+
// exporting a `Pick` picking optional properties which is turning them required somehow.)'
1718
sucrase({
1819
transforms: ['jsx', 'typescript'],
19-
}),
20+
} as unknown as RollupSucraseOptions),
2021
],
2122

2223
// We want to process as few files as possible, so as not to slow down the build any more than we have to. We need the
2324
// proxy module (living in the temporary file we've created) and the file we're wrapping not to be external, because
2425
// otherwise they won't be processed. (We need Rollup to process the former so that we can use the code, and we need
2526
// it to process the latter so it knows what exports to re-export from the proxy module.) Past that, we don't care, so
2627
// don't bother to process anything else.
27-
external: importPath => importPath !== SENTRY_PROXY_MODULE_NAME && importPath !== userModulePath,
28+
external: importPath => importPath !== proxyPath && importPath !== resourcePath,
2829

2930
// Prevent rollup from stressing out about TS's use of global `this` when polyfilling await. (TS will polyfill if the
3031
// user's tsconfig `target` is set to anything before `es2017`. See https://stackoverflow.com/a/72822340 and
@@ -65,19 +66,19 @@ const rollupOutputOptions: RollupOutputOptions = {
6566
* '<wrapped file>'` call into individual exports (which nextjs seems to need).
6667
*
6768
* @param tempProxyFilePath The path to the temporary file containing the proxy module code
68-
* @param userModulePath The path to the file being wrapped
69+
* @param resourcePath The path to the file being wrapped
6970
* @returns The processed proxy module code, or undefined if an error occurs
7071
*/
71-
export async function rollupize(userModulePath: string, templateCode: string): Promise<string | undefined> {
72+
export async function rollupize(tempProxyFilePath: string, resourcePath: string): Promise<string | undefined> {
7273
let finalBundle;
7374

7475
try {
75-
const intermediateBundle = await rollup(getRollupInputOptions(userModulePath, templateCode));
76+
const intermediateBundle = await rollup(getRollupInputOptions(tempProxyFilePath, resourcePath));
7677
finalBundle = await intermediateBundle.generate(rollupOutputOptions);
7778
} catch (err) {
7879
__DEBUG_BUILD__ &&
7980
logger.warn(
80-
`Could not wrap ${userModulePath}. An error occurred while processing the proxy module template:\n${err}`,
81+
`Could not wrap ${resourcePath}. An error occurred while processing the proxy module template:\n${err}`,
8182
);
8283
return undefined;
8384
}
@@ -91,7 +92,7 @@ export async function rollupize(userModulePath: string, templateCode: string): P
9192
// square brackets into underscores. Further, Rollup adds file extensions to bare-path-type import and export sources.
9293
// Because it assumes that everything will have already been processed, it always uses `.js` as the added extension.
9394
// We need to restore the original name and extension so that Webpack will be able to find the wrapped file.
94-
const resourceFilename = path.basename(userModulePath);
95+
const resourceFilename = path.basename(resourcePath);
9596
const mutatedResourceFilename = resourceFilename
9697
// `[\\[\\]]` is the character class containing `[` and `]`
9798
.replace(new RegExp('[\\[\\]]', 'g'), '_')

packages/nextjs/src/config/templates/apiProxyLoaderTemplate.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
*
55
* We use `__RESOURCE_PATH__` as a placeholder for the path to the file being wrapped. Because it's not a real package,
66
* this causes both TS and ESLint to complain, hence the pragma comments below.
7-
*
8-
* The `?__sentry_external__` is used to
9-
* 1) tell rollup to treat the import as external (i.e. not process it)
10-
* 2) tell webpack not to proxy this file again (avoiding an infinite loop)
117
*/
128

139
// @ts-ignore See above
1410
// eslint-disable-next-line import/no-unresolved
15-
import * as origModule from '__RESOURCE_PATH__?__sentry_external__';
11+
import * as origModule from '__RESOURCE_PATH__';
1612
import * as Sentry from '@sentry/nextjs';
1713
import type { PageConfig } from 'next';
1814

packages/nextjs/src/config/templates/pageProxyLoaderTemplate.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
*
55
* We use `__RESOURCE_PATH__` as a placeholder for the path to the file being wrapped. Because it's not a real package,
66
* this causes both TS and ESLint to complain, hence the pragma comments below.
7-
*
8-
* The `?__sentry_external__` is used to
9-
* 1) tell rollup to treat the import as external (i.e. not process it)
10-
* 2) tell webpack not to proxy this file again (avoiding an infinite loop)
117
*/
128

139
// @ts-ignore See above
1410
// eslint-disable-next-line import/no-unresolved
15-
import * as wrapee from '__RESOURCE_PATH__?__sentry_external__';
11+
import * as wrapee from '__RESOURCE_PATH__';
1612
import * as Sentry from '@sentry/nextjs';
1713
import type { GetServerSideProps, GetStaticProps, NextPage as NextPageComponent } from 'next';
1814

yarn.lock

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,11 +3351,6 @@
33513351
"@rollup/pluginutils" "^4.1.1"
33523352
sucrase "^3.20.0"
33533353

3354-
3355-
version "3.0.0"
3356-
resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.0.tgz#8c3f54b4ab4b267d9cd3dcbaedc58d4fd1deddca"
3357-
integrity sha512-K9KORe1myM62o0lKkNR4MmCxjwuAXsZEtIHpaILfv4kILXTOrXt/R2ha7PzMcCHPYdnkWPiBZK8ed4Zr3Ll5lQ==
3358-
33593354
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0":
33603355
version "3.1.0"
33613356
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"

0 commit comments

Comments
 (0)