| | import { bold, cyan, green, red, yellow } from '../../../../lib/picocolors' |
| | import { SimpleWebpackError } from './simpleWebpackError' |
| | import { |
| | createOriginalStackFrame, |
| | getIgnoredSources, |
| | } from '../../../../server/dev/middleware-webpack' |
| | import type { webpack } from 'next/dist/compiled/webpack/webpack' |
| | import type { RawSourceMap } from 'next/dist/compiled/source-map08' |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getModuleTrace(input: any, compilation: any) { |
| | const visitedModules = new Set() |
| | const moduleTrace = [] |
| | let current = input.module |
| | while (current) { |
| | if (visitedModules.has(current)) break |
| | visitedModules.add(current) |
| | const origin = compilation.moduleGraph.getIssuer(current) |
| | if (!origin) break |
| | moduleTrace.push({ origin, module: current }) |
| | current = origin |
| | } |
| |
|
| | return moduleTrace |
| | } |
| |
|
| | function sourceMapIgnoreListsEverything( |
| | sourceMap: RawSourceMap & { ignoreList?: number[] } |
| | ): boolean { |
| | return sourceMap.sources.length === sourceMap.ignoreList?.length |
| | } |
| |
|
| | async function getSourceFrame( |
| | input: any, |
| | fileName: any, |
| | compilation: any |
| | ): Promise<{ frame: string; line1: string; column1: string }> { |
| | try { |
| | const loc = |
| | input.loc || input.dependencies.map((d: any) => d.loc).filter(Boolean)[0] |
| | const module = input.module as webpack.Module |
| | const originalSource = module.originalSource() |
| | const sourceMap = originalSource?.map() ?? undefined |
| |
|
| | if (sourceMap) { |
| | const moduleId = compilation.chunkGraph.getModuleId(module) |
| |
|
| | const result = await createOriginalStackFrame({ |
| | ignoredByDefault: sourceMapIgnoreListsEverything(sourceMap), |
| | source: { |
| | type: 'bundle', |
| | sourceMap, |
| | ignoredSources: getIgnoredSources(sourceMap), |
| | compilation, |
| | moduleId, |
| | moduleURL: fileName, |
| | }, |
| | rootDirectory: compilation.options.context!, |
| | frame: { |
| | arguments: [], |
| | file: fileName, |
| | methodName: '', |
| | line1: loc.start.line, |
| | |
| | column1: (loc.start.column ?? 0) + 1, |
| | }, |
| | }) |
| |
|
| | return { |
| | frame: result?.originalCodeFrame ?? '', |
| | line1: result?.originalStackFrame?.line1?.toString() ?? '', |
| | column1: result?.originalStackFrame?.column1?.toString() ?? '', |
| | } |
| | } |
| | } catch {} |
| |
|
| | return { frame: '', line1: '', column1: '' } |
| | } |
| |
|
| | function getFormattedFileName( |
| | fileName: string, |
| | module: any, |
| | lineNumber?: string, |
| | column?: string |
| | ): string { |
| | if ( |
| | module.loaders?.find((loader: any) => |
| | /next-font-loader[/\\]index.js/.test(loader.loader) |
| | ) |
| | ) { |
| | |
| | |
| | return JSON.parse(module.resourceResolveData.query.slice(1)).path |
| | } else { |
| | let formattedFileName: string = cyan(fileName) |
| | if (lineNumber && column) { |
| | formattedFileName += `:${yellow(lineNumber)}:${yellow(column)}` |
| | } |
| |
|
| | return formattedFileName |
| | } |
| | } |
| |
|
| | export async function getNotFoundError( |
| | compilation: webpack.Compilation, |
| | input: any, |
| | fileName: string, |
| | module: any |
| | ) { |
| | if ( |
| | input.name !== 'ModuleNotFoundError' && |
| | !( |
| | input.name === 'ModuleBuildError' && |
| | /Error: Can't resolve '.+' in /.test(input.message) |
| | ) |
| | ) { |
| | return false |
| | } |
| |
|
| | try { |
| | const { frame, line1, column1 } = await getSourceFrame( |
| | input, |
| | fileName, |
| | compilation |
| | ) |
| |
|
| | const errorMessage = input.error.message |
| | .replace(/ in '.*?'/, '') |
| | .replace(/Can't resolve '(.*)'/, `Can't resolve '${green('$1')}'`) |
| |
|
| | const importTrace = () => { |
| | const moduleTrace = getModuleTrace(input, compilation) |
| | .map(({ origin }) => |
| | origin.readableIdentifier(compilation.requestShortener) |
| | ) |
| | .filter( |
| | (name) => |
| | name && |
| | !/next-(app|middleware|client-pages|route|flight-(client|server|client-entry))-loader/.test( |
| | name |
| | ) && |
| | !/css-loader.+\.js/.test(name) |
| | ) |
| | if (moduleTrace.length === 0) return '' |
| |
|
| | return `\nImport trace for requested module:\n${moduleTrace.join('\n')}` |
| | } |
| |
|
| | let message = |
| | red(bold('Module not found')) + |
| | `: ${errorMessage}` + |
| | '\n' + |
| | frame + |
| | (frame !== '' ? '\n' : '') + |
| | '\nhttps://nextjs.org/docs/messages/module-not-found\n' + |
| | importTrace() |
| |
|
| | const formattedFileName = getFormattedFileName( |
| | fileName, |
| | module, |
| | line1, |
| | column1 |
| | ) |
| |
|
| | return new SimpleWebpackError(formattedFileName, message) |
| | } catch (err) { |
| | |
| | return input |
| | } |
| | } |
| |
|
| | export async function getImageError( |
| | compilation: any, |
| | input: any, |
| | err: Error |
| | ): Promise<SimpleWebpackError | false> { |
| | if (err.name !== 'InvalidImageFormatError') { |
| | return false |
| | } |
| |
|
| | const moduleTrace = getModuleTrace(input, compilation) |
| | const { origin, module } = moduleTrace[0] || {} |
| | if (!origin || !module) { |
| | return false |
| | } |
| | const page = origin.rawRequest.replace(/^private-next-pages/, './pages') |
| | const importedFile = module.rawRequest |
| | const source = origin.originalSource().buffer().toString('utf8') as string |
| | let lineNumber = -1 |
| | source.split('\n').some((line) => { |
| | lineNumber++ |
| | return line.includes(importedFile) |
| | }) |
| | return new SimpleWebpackError( |
| | `${cyan(page)}:${yellow(lineNumber.toString())}`, |
| | red(bold('Error')).concat( |
| | `: Image import "${importedFile}" is not a valid image file. The image may be corrupted or an unsupported format.` |
| | ) |
| | ) |
| | } |
| |
|