Skip to content
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

produce source maps for middlewares #34409

Merged
merged 8 commits into from Feb 24, 2022
7 changes: 7 additions & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -51,6 +51,7 @@ import { getRawPageExtensions } from './utils'
import browserslist from 'next/dist/compiled/browserslist'
import loadJsConfig from './load-jsconfig'
import { shouldUseReactRoot } from '../server/config'
import { getMiddlewareSourceMapPlugins } from './webpack/plugins/middleware-source-maps-plugin'

const watchOptions = Object.freeze({
aggregateTimeout: 5,
Expand Down Expand Up @@ -1272,6 +1273,12 @@ export default async function getBaseWebpackConfig(
].filter(Boolean),
},
plugins: [
...(!dev &&
!isServer &&
!!config.experimental.middlewareSourceMaps &&
!config.productionBrowserSourceMaps
? getMiddlewareSourceMapPlugins()
: []),
hasReactRefresh && new ReactRefreshWebpackPlugin(webpack),
// Makes sure `Buffer` and `process` are polyfilled in client and flight bundles (same behavior as webpack 4)
targetWeb &&
Expand Down
@@ -0,0 +1,38 @@
import { webpack } from 'next/dist/compiled/webpack/webpack'
import type { webpack5 } from 'next/dist/compiled/webpack/webpack'

/**
* Produce source maps for middlewares.
* Currently we use the same compiler for browser and middlewares,
*/
export const getMiddlewareSourceMapPlugins = () => {
return [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
include: [
// Middlewares are the only ones who have `server/pages/[name]` as their filename
/^server\/pages\//,
// All middleware chunks
/^server\/middleware-chunks\//,
],
}),
new MiddlewareSourceMapsPlugin(),
]
}

/**
* Produce source maps for middlewares.
* Currently we use the same compiler for browser and middlewares,
* so we can avoid having the custom plugins if the browser source maps
* are emitted.
*/
class MiddlewareSourceMapsPlugin {
apply(compiler: webpack5.Compiler): void {
const PLUGIN_NAME = 'NextJsMiddlewareSourceMapsPlugin'
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.buildModule.tap(PLUGIN_NAME, (module) => {
module.useSourceMap = module.layer === 'middleware'
Copy link
Member

Choose a reason for hiding this comment

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

This seems to override other conditionals if you have sourcemaps enabled in a different way 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Probably needs

if(!module.useSourceMap) to make sure you don't disable other plugins enabling sourcemaps for certain modules.

})
})
}
}
1 change: 1 addition & 0 deletions packages/next/server/config-shared.ts
Expand Up @@ -106,6 +106,7 @@ export interface ExperimentalConfig {
urlImports?: NonNullable<webpack5.Configuration['experiments']>['buildHttp']
outputFileTracingRoot?: string
outputStandalone?: boolean
middlewareSourceMaps?: boolean
}

/**
Expand Down
63 changes: 63 additions & 0 deletions test/production/generate-middleware-source-maps/index.test.ts
@@ -0,0 +1,63 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import fs from 'fs-extra'
import path from 'path'

describe('experimental.middlewareSourceMaps: true', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
nextConfig: {
experimental: {
middlewareSourceMaps: true,
},
},
files: {
'pages/_middleware.js': `
export default function middleware() {
return new Response("Hello, world!");
}
`,
},
dependencies: {},
})
})
afterAll(() => next.destroy())

it('generates a source map', async () => {
const middlewarePath = path.resolve(
next.testDir,
'.next/server/pages/_middleware.js'
)
expect(await fs.pathExists(middlewarePath)).toEqual(true)
expect(await fs.pathExists(`${middlewarePath}.map`)).toEqual(true)
})
})

describe('experimental.middlewareSourceMaps: false', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/_middleware.js': `
export default function middleware() {
return new Response("Hello, world!");
}
`,
},
dependencies: {},
})
})
afterAll(() => next.destroy())

it('does not generate a source map', async () => {
const middlewarePath = path.resolve(
next.testDir,
'.next/server/pages/_middleware.js'
)
expect(await fs.pathExists(middlewarePath)).toEqual(true)
expect(await fs.pathExists(`${middlewarePath}.map`)).toEqual(false)
})
})