-
Notifications
You must be signed in to change notification settings - Fork 31.9k
Expand file tree
/
Copy pathtrace-cli-startup.js
More file actions
executable file
·139 lines (115 loc) · 3.98 KB
/
Copy pathtrace-cli-startup.js
File metadata and controls
executable file
·139 lines (115 loc) · 3.98 KB
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env node
/**
* CLI Startup Tracer
*
* Uses the V8 Inspector API to trace module loading at CLI startup.
* This helps identify which modules are being loaded eagerly.
*
* Usage:
* node scripts/trace-cli-startup.js [--command=dev|build|--help]
*/
const inspector = require('inspector')
const fs = require('fs')
const path = require('path')
const args = process.argv.slice(2)
const getArg = (name, defaultValue) => {
const arg = args.find((a) => a.startsWith(`--${name}=`))
return arg ? arg.split('=')[1] : defaultValue
}
const command = getArg('command', '--help')
const outputDir = path.join(process.cwd(), 'profiles')
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true })
}
console.log('\x1b[34m=== Next.js CLI Startup Trace ===\x1b[0m')
console.log(`Command: next ${command}`)
console.log(`Output directory: ${outputDir}`)
console.log('')
// Start CPU profiling
const session = new inspector.Session()
session.connect()
// Track module loading via require hook
const Module = require('module')
const originalRequire = Module.prototype.require
const loadedModules = []
const moduleLoadTimes = []
Module.prototype.require = function (id) {
const start = process.hrtime.bigint()
const result = originalRequire.apply(this, arguments)
const end = process.hrtime.bigint()
const durationMs = Number(end - start) / 1e6
// Get the resolved path
let resolvedPath = id
try {
resolvedPath = require.resolve(id, { paths: [this.path || process.cwd()] })
} catch {}
// Filter to show only Next.js-related modules
if (resolvedPath.includes('next/dist/') || resolvedPath.includes('@next/')) {
const shortPath = resolvedPath.includes('next/dist/')
? resolvedPath.split('next/dist/')[1]
: resolvedPath
if (!loadedModules.includes(shortPath)) {
loadedModules.push(shortPath)
moduleLoadTimes.push({ module: shortPath, time: durationMs })
}
}
return result
}
// Save original process.exit and intercept to prevent CLI from exiting mid-profile
const originalExit = process.exit
process.exit = () => {
// Don't actually exit during profiling - we want to capture the full profile
}
// Start profiling
session.post('Profiler.enable', () => {
session.post('Profiler.start', () => {
console.log('Starting CLI with profiling...')
console.log('')
const startTime = process.hrtime.bigint()
// Load the CLI
try {
process.argv = [process.argv[0], 'next', command]
require('../packages/next/dist/bin/next')
} catch (e) {
// Expected - CLI might throw
}
const endTime = process.hrtime.bigint()
const totalMs = Number(endTime - startTime) / 1e6
// Stop profiling and save
session.post('Profiler.stop', (err, { profile }) => {
if (err) {
console.error('Error stopping profiler:', err)
} else {
const profilePath = path.join(
outputDir,
`cli-startup-${Date.now()}.cpuprofile`
)
fs.writeFileSync(profilePath, JSON.stringify(profile))
console.log(`\x1b[32mProfile saved:\x1b[0m ${profilePath}`)
}
// Print results
console.log('')
console.log(`\x1b[32mTotal startup time:\x1b[0m ${totalMs.toFixed(2)}ms`)
console.log('')
console.log(`\x1b[33mModules loaded (${loadedModules.length}):\x1b[0m`)
console.log('='.repeat(70))
// Sort by load time
moduleLoadTimes.sort((a, b) => b.time - a.time)
moduleLoadTimes.slice(0, 30).forEach((m, i) => {
const timeStr =
m.time > 1
? `${m.time.toFixed(1)}ms`
: `${(m.time * 1000).toFixed(0)}μs`
console.log(`${String(i + 1).padStart(2)}. ${m.module} (${timeStr})`)
})
console.log('')
console.log(`\x1b[33mAll loaded modules:\x1b[0m`)
console.log(loadedModules.join('\n'))
// Restore original require
Module.prototype.require = originalRequire
session.disconnect()
// Exit cleanly now that profiling is complete
originalExit(0)
})
})
})