Skip to content

Commit

Permalink
Use webpack resolve with nft (#29342)
Browse files Browse the repository at this point in the history
This is a follow-up to #29341 to add using the webpack resolver while tracing for better performance.
  • Loading branch information
ijjk committed Sep 24, 2021
1 parent 5dbb870 commit a986581
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
9 changes: 6 additions & 3 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function attachReactRefresh(
}
}

const NODE_RESOLVE_OPTIONS = {
export const NODE_RESOLVE_OPTIONS = {
dependencyType: 'commonjs',
modules: ['node_modules'],
alias: false,
Expand All @@ -196,7 +196,7 @@ const NODE_RESOLVE_OPTIONS = {
restrictions: [],
}

const NODE_ESM_RESOLVE_OPTIONS = {
export const NODE_ESM_RESOLVE_OPTIONS = {
...NODE_RESOLVE_OPTIONS,
dependencyType: 'esm',
conditionNames: ['node', 'import'],
Expand Down Expand Up @@ -1293,7 +1293,10 @@ export default async function getBaseWebpackConfig(
isServer &&
!dev &&
isWebpack5 &&
new TraceEntryPointsPlugin({ appDir: dir }),
new TraceEntryPointsPlugin({
appDir: dir,
esmExternals: config.experimental.esmExternals,
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
Expand Down
102 changes: 100 additions & 2 deletions packages/next/build/webpack/plugins/next-trace-entrypoints-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {
isWebpack5,
sources,
} from 'next/dist/compiled/webpack/webpack'
import {
NODE_ESM_RESOLVE_OPTIONS,
NODE_RESOLVE_OPTIONS,
} from '../../webpack-config'
import { NextConfigComplete } from '../../../server/config-shared'

const PLUGIN_NAME = 'TraceEntryPointsPlugin'
const TRACE_IGNORES = [
Expand All @@ -33,16 +38,20 @@ export class TraceEntryPointsPlugin implements webpack.Plugin {
private appDir: string
private entryTraces: Map<string, string[]>
private excludeFiles: string[]
private esmExternals: NextConfigComplete['experimental']['esmExternals']

constructor({
appDir,
excludeFiles,
esmExternals,
}: {
appDir: string
excludeFiles?: string[]
esmExternals?: NextConfigComplete['experimental']['esmExternals']
}) {
this.appDir = appDir
this.entryTraces = new Map()
this.esmExternals = esmExternals
this.excludeFiles = excludeFiles || []
}

Expand Down Expand Up @@ -99,7 +108,12 @@ export class TraceEntryPointsPlugin implements webpack.Plugin {

tapfinishModules(
compilation: webpack.compilation.Compilation,
traceEntrypointsPluginSpan: Span
traceEntrypointsPluginSpan: Span,
doResolve?: (
request: string,
context: string,
isEsm?: boolean
) => Promise<string>
) {
compilation.hooks.finishModules.tapAsync(
PLUGIN_NAME,
Expand Down Expand Up @@ -282,6 +296,10 @@ export class TraceEntryPointsPlugin implements webpack.Plugin {
readFile,
readlink,
stat,
resolve: doResolve
? (id, parent, _job, isCjs) =>
doResolve(id, nodePath.dirname(parent), !isCjs)
: undefined,
ignore: [...TRACE_IGNORES, ...this.excludeFiles],
mixedModules: true,
})
Expand Down Expand Up @@ -335,8 +353,88 @@ export class TraceEntryPointsPlugin implements webpack.Plugin {
)
}
)
const resolver = compilation.resolverFactory.get('normal')
const looseEsmExternals = this.esmExternals === 'loose'

const doResolve = async (
request: string,
context: string,
isEsmRequested?: boolean
): Promise<string> => {
const preferEsm = isEsmRequested && this.esmExternals
const curResolver = resolver.withOptions(
preferEsm ? NODE_ESM_RESOLVE_OPTIONS : NODE_RESOLVE_OPTIONS
)

let res: string = ''
try {
res = await new Promise((resolve, reject) => {
curResolver.resolve(
{},
context,
request,
{
fileDependencies: compilation.fileDependencies,
missingDependencies: compilation.missingDependencies,
contextDependencies: compilation.contextDependencies,
},
(err: any, result: string) => {
if (err) reject(err)
else resolve(result)
}
)
})
} catch (err) {
if (!(isEsmRequested && looseEsmExternals)) {
throw err
}
// continue to attempt alternative resolving
}

this.tapfinishModules(compilation, traceEntrypointsPluginSpan)
if (!res && isEsmRequested && looseEsmExternals) {
const altResolver = resolver.withOptions(
preferEsm ? NODE_RESOLVE_OPTIONS : NODE_ESM_RESOLVE_OPTIONS
)

res = await new Promise((resolve, reject) => {
altResolver.resolve(
{},
context,
request,
{
fileDependencies: compilation.fileDependencies,
missingDependencies: compilation.missingDependencies,
contextDependencies: compilation.contextDependencies,
},
(err: any, result: string) => {
if (err) reject(err)
else resolve(result)
}
)
})
}

if (!res) {
// we should not get here as one of the two above resolves should
// have thrown but this is here as a safeguard
throw new Error(
'invariant: failed to resolve ' +
JSON.stringify({
isEsmRequested,
looseEsmExternals,
request,
res,
})
)
}
return res
}

this.tapfinishModules(
compilation,
traceEntrypointsPluginSpan,
doResolve
)
})
})
} else {
Expand Down

0 comments on commit a986581

Please sign in to comment.