File size: 2,640 Bytes
b91e262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import type { webpack } from 'next/dist/compiled/webpack/webpack'
import loaderUtils from 'next/dist/compiled/loader-utils3'
import { relative } from 'path'

function formatModule(compiler: webpack.Compiler, module: any) {
  const relativePath = relative(compiler.context, module.resource).replace(
    /\?.+$/,
    ''
  )
  return loaderUtils.isUrlRequest(relativePath)
    ? loaderUtils.urlToRequest(relativePath)
    : relativePath
}

export function formatModuleTrace(
  compiler: webpack.Compiler,
  moduleTrace: any[]
) {
  let importTrace: string[] = []
  let firstExternalModule: any
  for (let i = moduleTrace.length - 1; i >= 0; i--) {
    const mod = moduleTrace[i]
    if (!mod.resource) continue

    if (!mod.resource.includes('node_modules/')) {
      importTrace.unshift(formatModule(compiler, mod))
    } else {
      firstExternalModule = mod
      break
    }
  }

  let invalidImportMessage = ''
  if (firstExternalModule) {
    const firstExternalPackageName =
      firstExternalModule.resourceResolveData?.descriptionFileData?.name

    if (firstExternalPackageName === 'styled-jsx') {
      invalidImportMessage += `\n\nThe error was caused by using 'styled-jsx' in '${importTrace[0]}'. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.`
    } else {
      let formattedExternalFile =
        firstExternalModule.resource.split('node_modules')
      formattedExternalFile =
        formattedExternalFile[formattedExternalFile.length - 1]

      invalidImportMessage += `\n\nThe error was caused by importing '${formattedExternalFile.slice(
        1
      )}' in '${importTrace[0]}'.`
    }
  }

  return {
    lastInternalFileName: importTrace[0],
    invalidImportMessage,
    formattedModuleTrace: importTrace.map((mod) => '  ' + mod).join('\n'),
  }
}

export function getModuleTrace(
  module: any,
  compilation: webpack.Compilation,
  compiler: webpack.Compiler
) {
  // Get the module trace:
  // https://cs.github.com/webpack/webpack/blob/9fcaa243573005d6fdece9a3f8d89a0e8b399613/lib/stats/DefaultStatsFactoryPlugin.js#L414
  const visitedModules = new Set()
  const moduleTrace = []

  let current = module
  let isPagesDir = false
  while (current) {
    if (visitedModules.has(current)) break
    if (/[\\/]pages/.test(current.resource.replace(compiler.context, ''))) {
      isPagesDir = true
    }
    visitedModules.add(current)
    moduleTrace.push(current)
    const origin = compilation.moduleGraph.getIssuer(current)
    if (!origin) break
    current = origin
  }

  return {
    moduleTrace,
    isPagesDir,
  }
}