You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			473 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			473 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | #!/usr/bin/env node
 | ||
|  | "use strict"; | ||
|  | Object.defineProperty(exports, "__esModule", { | ||
|  |     value: true | ||
|  | }); | ||
|  | Object.defineProperty(exports, "nextInfo", { | ||
|  |     enumerable: true, | ||
|  |     get: function() { | ||
|  |         return nextInfo; | ||
|  |     } | ||
|  | }); | ||
|  | const _os = /*#__PURE__*/ _interop_require_default(require("os")); | ||
|  | const _child_process = /*#__PURE__*/ _interop_require_default(require("child_process")); | ||
|  | const _picocolors = require("../lib/picocolors"); | ||
|  | const _constants = require("../shared/lib/constants"); | ||
|  | const _config = /*#__PURE__*/ _interop_require_default(require("../server/config")); | ||
|  | function _interop_require_default(obj) { | ||
|  |     return obj && obj.__esModule ? obj : { | ||
|  |         default: obj | ||
|  |     }; | ||
|  | } | ||
|  | const dir = process.cwd(); | ||
|  | function getPackageVersion(packageName) { | ||
|  |     try { | ||
|  |         return require(`${packageName}/package.json`).version; | ||
|  |     } catch  { | ||
|  |         return "N/A"; | ||
|  |     } | ||
|  | } | ||
|  | async function getNextConfig() { | ||
|  |     var _config_experimental; | ||
|  |     const config = await (0, _config.default)(_constants.PHASE_INFO, dir); | ||
|  |     return { | ||
|  |         output: config.output ?? "N/A", | ||
|  |         experimental: { | ||
|  |             useWasmBinary: (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.useWasmBinary | ||
|  |         } | ||
|  |     }; | ||
|  | } | ||
|  | /** | ||
|  |  * Returns the version of the specified binary, by supplying `--version` argument. | ||
|  |  * N/A if it fails to run the binary. | ||
|  |  */ function getBinaryVersion(binaryName) { | ||
|  |     try { | ||
|  |         return _child_process.default.execFileSync(binaryName, [ | ||
|  |             "--version" | ||
|  |         ]).toString().trim(); | ||
|  |     } catch  { | ||
|  |         return "N/A"; | ||
|  |     } | ||
|  | } | ||
|  | function printHelp() { | ||
|  |     console.log(`
 | ||
|  | Description | ||
|  |   Prints relevant details about the current system which can be used to report Next.js bugs | ||
|  | 
 | ||
|  | Usage | ||
|  |   $ next info | ||
|  | 
 | ||
|  | Options | ||
|  |   --help, -h    Displays this message | ||
|  |   --verbose     Collect additional information for debugging | ||
|  | 
 | ||
|  | Learn more: ${(0, _picocolors.cyan)("https://nextjs.org/docs/api-reference/cli#info")}`);
 | ||
|  | } | ||
|  | /** | ||
|  |  * Collect basic next.js installation information and print it to stdout. | ||
|  |  */ async function printDefaultInfo() { | ||
|  |     const installedRelease = getPackageVersion("next"); | ||
|  |     const nextConfig = await getNextConfig(); | ||
|  |     console.log(`
 | ||
|  | Operating System: | ||
|  |   Platform: ${_os.default.platform()} | ||
|  |   Arch: ${_os.default.arch()} | ||
|  |   Version: ${_os.default.version()} | ||
|  | Binaries: | ||
|  |   Node: ${process.versions.node} | ||
|  |   npm: ${getBinaryVersion("npm")} | ||
|  |   Yarn: ${getBinaryVersion("yarn")} | ||
|  |   pnpm: ${getBinaryVersion("pnpm")} | ||
|  | Relevant Packages: | ||
|  |   next: ${installedRelease} | ||
|  |   eslint-config-next: ${getPackageVersion("eslint-config-next")} | ||
|  |   react: ${getPackageVersion("react")} | ||
|  |   react-dom: ${getPackageVersion("react-dom")} | ||
|  |   typescript: ${getPackageVersion("typescript")} | ||
|  | Next.js Config: | ||
|  |   output: ${nextConfig.output} | ||
|  | 
 | ||
|  | `);
 | ||
|  |     try { | ||
|  |         const res = await fetch("https://api.github.com/repos/vercel/next.js/releases"); | ||
|  |         const releases = await res.json(); | ||
|  |         const newestRelease = releases[0].tag_name.replace(/^v/, ""); | ||
|  |         if (installedRelease !== newestRelease) { | ||
|  |             console.warn(`${(0, _picocolors.yellow)((0, _picocolors.bold)("warn"))}  - Latest canary version not detected, detected: "${installedRelease}", newest: "${newestRelease}".
 | ||
|  |         Please try the latest canary version (\`npm install next@canary\`) to confirm the issue still exists before creating a new issue.
 | ||
|  |         Read more - https://nextjs.org/docs/messages/opening-an-issue`);
 | ||
|  |         } | ||
|  |     } catch (e) { | ||
|  |         console.warn(`${(0, _picocolors.yellow)((0, _picocolors.bold)("warn"))}  - Failed to fetch latest canary version. (Reason: ${e.message}.)
 | ||
|  |       Detected "${installedRelease}". Visit https://github.com/vercel/next.js/releases.
 | ||
|  |       Make sure to try the latest canary version (eg.: \`npm install next@canary\`) to confirm the issue still exists before creating a new issue.
 | ||
|  |       Read more - https://nextjs.org/docs/messages/opening-an-issue`);
 | ||
|  |     } | ||
|  | } | ||
|  | /** | ||
|  |  * Using system-installed tools per each platform, trying to read shared dependencies of next-swc. | ||
|  |  * This is mainly for debugging DLOPEN failure. | ||
|  |  * | ||
|  |  * We don't / can't install these tools by ourselves, will skip the check if we can't find them. | ||
|  |  */ async function runSharedDependencyCheck(tools, skipMessage) { | ||
|  |     var _getSupportedArchTriples_currentPlatform; | ||
|  |     const currentPlatform = _os.default.platform(); | ||
|  |     const spawn = require("next/dist/compiled/cross-spawn"); | ||
|  |     const { getSupportedArchTriples } = require("../build/swc"); | ||
|  |     const triples = ((_getSupportedArchTriples_currentPlatform = getSupportedArchTriples()[currentPlatform]) == null ? void 0 : _getSupportedArchTriples_currentPlatform[_os.default.arch()]) ?? []; | ||
|  |     // First, check if system have a tool installed. We can't install these by our own.
 | ||
|  |     const availableTools = []; | ||
|  |     for (const tool of tools){ | ||
|  |         try { | ||
|  |             const check = spawn.sync(tool.bin, tool.checkArgs); | ||
|  |             if (check.status === 0) { | ||
|  |                 availableTools.push(tool); | ||
|  |             } | ||
|  |         } catch  { | ||
|  |         // ignore if existence check fails
 | ||
|  |         } | ||
|  |     } | ||
|  |     if (availableTools.length === 0) { | ||
|  |         return { | ||
|  |             messages: skipMessage, | ||
|  |             result: "skipped" | ||
|  |         }; | ||
|  |     } | ||
|  |     const outputs = []; | ||
|  |     let result = "fail"; | ||
|  |     for (const triple of triples){ | ||
|  |         const triplePkgName = `@next/swc-${triple.platformArchABI}`; | ||
|  |         let resolved; | ||
|  |         try { | ||
|  |             resolved = require.resolve(triplePkgName); | ||
|  |         } catch (e) { | ||
|  |             return { | ||
|  |                 messages: "Cannot find next-swc installation, skipping dependencies check", | ||
|  |                 result: "skipped" | ||
|  |             }; | ||
|  |         } | ||
|  |         for (const tool of availableTools){ | ||
|  |             const proc = spawn(tool.bin, [ | ||
|  |                 ...tool.args, | ||
|  |                 resolved | ||
|  |             ]); | ||
|  |             outputs.push(`Running ${tool.bin} ------------- `); | ||
|  |             // Captures output, doesn't matter if it fails or not since we'll forward both to output.
 | ||
|  |             const procPromise = new Promise((resolve)=>{ | ||
|  |                 proc.stdout.on("data", function(data) { | ||
|  |                     outputs.push(data); | ||
|  |                 }); | ||
|  |                 proc.stderr.on("data", function(data) { | ||
|  |                     outputs.push(data); | ||
|  |                 }); | ||
|  |                 proc.on("close", (c)=>resolve(c)); | ||
|  |             }); | ||
|  |             let code = await procPromise; | ||
|  |             if (code === 0) { | ||
|  |                 result = "pass"; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |     return { | ||
|  |         output: outputs.join("\n"), | ||
|  |         result | ||
|  |     }; | ||
|  | } | ||
|  | /** | ||
|  |  * Collect additional diagnostics information. | ||
|  |  */ async function printVerbose() { | ||
|  |     const fs = require("fs"); | ||
|  |     const currentPlatform = _os.default.platform(); | ||
|  |     if (currentPlatform !== "win32" && currentPlatform !== "linux" && currentPlatform !== "darwin") { | ||
|  |         console.log("Unsupported platform, only win32, linux, darwin are supported."); | ||
|  |         return; | ||
|  |     } | ||
|  |     // List of tasks to run.
 | ||
|  |     const tasks = [ | ||
|  |         { | ||
|  |             title: "Host system information", | ||
|  |             scripts: { | ||
|  |                 default: async ()=>{ | ||
|  |                     // Node.js diagnostic report contains basic information, i.e OS version, CPU architecture, etc.
 | ||
|  |                     // Only collect few addtional details here.
 | ||
|  |                     const isWsl = require("next/dist/compiled/is-wsl"); | ||
|  |                     const ciInfo = require("next/dist/compiled/ci-info"); | ||
|  |                     const isDocker = require("next/dist/compiled/is-docker"); | ||
|  |                     const output = `
 | ||
|  |   WSL: ${isWsl} | ||
|  |   Docker: ${isDocker()} | ||
|  |   CI: ${ciInfo.isCI ? ciInfo.name || "unknown" : "false"} | ||
|  | `;
 | ||
|  |                     return { | ||
|  |                         output, | ||
|  |                         result: "pass" | ||
|  |                     }; | ||
|  |                 } | ||
|  |             } | ||
|  |         }, | ||
|  |         { | ||
|  |             title: "Next.js installation", | ||
|  |             scripts: { | ||
|  |                 default: async ()=>{ | ||
|  |                     const installedRelease = getPackageVersion("next"); | ||
|  |                     const nextConfig = await getNextConfig(); | ||
|  |                     const output = `
 | ||
|  |   Binaries: | ||
|  |     Node: ${process.versions.node} | ||
|  |     npm: ${getBinaryVersion("npm")} | ||
|  |     Yarn: ${getBinaryVersion("yarn")} | ||
|  |     pnpm: ${getBinaryVersion("pnpm")} | ||
|  |   Relevant Packages: | ||
|  |     next: ${installedRelease} | ||
|  |     eslint-config-next: ${getPackageVersion("eslint-config-next")} | ||
|  |     react: ${getPackageVersion("react")} | ||
|  |     react-dom: ${getPackageVersion("react-dom")} | ||
|  |     typescript: ${getPackageVersion("typescript")} | ||
|  |   Next.js Config: | ||
|  |     output: ${nextConfig.output} | ||
|  | 
 | ||
|  | `;
 | ||
|  |                     return { | ||
|  |                         output, | ||
|  |                         result: "pass" | ||
|  |                     }; | ||
|  |                 } | ||
|  |             } | ||
|  |         }, | ||
|  |         { | ||
|  |             title: "Node.js diagnostic report", | ||
|  |             scripts: { | ||
|  |                 default: async ()=>{ | ||
|  |                     var _process_report; | ||
|  |                     const report = (_process_report = process.report) == null ? void 0 : _process_report.getReport(); | ||
|  |                     if (!report) { | ||
|  |                         return { | ||
|  |                             messages: "Node.js diagnostic report is not available.", | ||
|  |                             result: "fail" | ||
|  |                         }; | ||
|  |                     } | ||
|  |                     const { header, javascriptHeap, sharedObjects } = report; | ||
|  |                     // Delete some fields potentially containing sensitive information.
 | ||
|  |                     header == null ? true : delete header.cwd; | ||
|  |                     header == null ? true : delete header.commandLine; | ||
|  |                     header == null ? true : delete header.host; | ||
|  |                     header == null ? true : delete header.cpus; | ||
|  |                     header == null ? true : delete header.networkInterfaces; | ||
|  |                     const reportSummary = { | ||
|  |                         header, | ||
|  |                         javascriptHeap, | ||
|  |                         sharedObjects | ||
|  |                     }; | ||
|  |                     return { | ||
|  |                         output: JSON.stringify(reportSummary, null, 2), | ||
|  |                         result: "pass" | ||
|  |                     }; | ||
|  |                 } | ||
|  |             } | ||
|  |         }, | ||
|  |         { | ||
|  |             title: "next-swc installation", | ||
|  |             scripts: { | ||
|  |                 default: async ()=>{ | ||
|  |                     var _platformArchTriples_currentPlatform; | ||
|  |                     const output = []; | ||
|  |                     // First, try to load next-swc via loadBindings.
 | ||
|  |                     try { | ||
|  |                         var _nextConfig_experimental; | ||
|  |                         let nextConfig = await getNextConfig(); | ||
|  |                         const { loadBindings } = require("../build/swc"); | ||
|  |                         const bindings = await loadBindings((_nextConfig_experimental = nextConfig.experimental) == null ? void 0 : _nextConfig_experimental.useWasmBinary); | ||
|  |                         // Run arbitary function to verify the bindings are loaded correctly.
 | ||
|  |                         const target = bindings.getTargetTriple(); | ||
|  |                         // We think next-swc is installed correctly if getTargetTriple returns.
 | ||
|  |                         return { | ||
|  |                             output: `next-swc is installed correctly for ${target}`, | ||
|  |                             result: "pass" | ||
|  |                         }; | ||
|  |                     } catch (e) { | ||
|  |                         output.push(`loadBindings() failed: ${e.message}`); | ||
|  |                     } | ||
|  |                     const { platformArchTriples } = require("next/dist/compiled/@napi-rs/triples"); | ||
|  |                     const triples = (_platformArchTriples_currentPlatform = platformArchTriples[currentPlatform]) == null ? void 0 : _platformArchTriples_currentPlatform[_os.default.arch()]; | ||
|  |                     if (!triples || triples.length === 0) { | ||
|  |                         return { | ||
|  |                             messages: `No target triples found for ${currentPlatform} / ${_os.default.arch()}`, | ||
|  |                             result: "fail" | ||
|  |                         }; | ||
|  |                     } | ||
|  |                     // Trying to manually resolve corresponding target triples to see if bindings are physically located.
 | ||
|  |                     const path = require("path"); | ||
|  |                     let fallbackBindingsDirectory; | ||
|  |                     try { | ||
|  |                         const nextPath = path.dirname(require.resolve("next/package.json")); | ||
|  |                         fallbackBindingsDirectory = path.join(nextPath, "next-swc-fallback"); | ||
|  |                     } catch (e) { | ||
|  |                     // Not able to locate next package from current running location, skipping fallback bindings check.
 | ||
|  |                     } | ||
|  |                     const tryResolve = (pkgName)=>{ | ||
|  |                         try { | ||
|  |                             const resolved = require.resolve(pkgName); | ||
|  |                             const fileExists = fs.existsSync(resolved); | ||
|  |                             let loadError; | ||
|  |                             let loadSuccess; | ||
|  |                             try { | ||
|  |                                 loadSuccess = !!require(resolved).getTargetTriple(); | ||
|  |                             } catch (e) { | ||
|  |                                 loadError = e.message; | ||
|  |                             } | ||
|  |                             output.push(`${pkgName} exists: ${fileExists} for the triple ${loadSuccess}`); | ||
|  |                             if (loadError) { | ||
|  |                                 output.push(`${pkgName} load failed: ${loadError ?? "unknown"}`); | ||
|  |                             } | ||
|  |                             if (loadSuccess) { | ||
|  |                                 return true; | ||
|  |                             } | ||
|  |                         } catch (e) { | ||
|  |                             output.push(`${pkgName} resolve failed: ${e.message ?? "unknown"}`); | ||
|  |                         } | ||
|  |                         return false; | ||
|  |                     }; | ||
|  |                     for (const triple of triples){ | ||
|  |                         const triplePkgName = `@next/swc-${triple.platformArchABI}`; | ||
|  |                         // Check installed optional dependencies. This is the normal way package being installed.
 | ||
|  |                         // For the targets have multiple triples (gnu / musl), if any of them loads successfully, we consider as installed.
 | ||
|  |                         if (tryResolve(triplePkgName)) { | ||
|  |                             break; | ||
|  |                         } | ||
|  |                         // Check if fallback binaries are installed.
 | ||
|  |                         if (!fallbackBindingsDirectory) { | ||
|  |                             continue; | ||
|  |                         } | ||
|  |                         tryResolve(path.join(fallbackBindingsDirectory, triplePkgName)); | ||
|  |                     } | ||
|  |                     return { | ||
|  |                         output: output.join("\n"), | ||
|  |                         result: "pass" | ||
|  |                     }; | ||
|  |                 } | ||
|  |             } | ||
|  |         }, | ||
|  |         { | ||
|  |             // For the simplicity, we only check the correctly installed optional dependencies -
 | ||
|  |             // as this is mainly for checking DLOPEN failure. If user hit MODULE_NOT_FOUND,
 | ||
|  |             // expect above next-swc installation would give some hint instead.
 | ||
|  |             title: "next-swc shared object dependencies", | ||
|  |             scripts: { | ||
|  |                 linux: async ()=>{ | ||
|  |                     const skipMessage = "This diagnostics uses system-installed tools (ldd) to check next-swc dependencies, but it is not found. Skipping dependencies check."; | ||
|  |                     return await runSharedDependencyCheck([ | ||
|  |                         { | ||
|  |                             bin: "ldd", | ||
|  |                             checkArgs: [ | ||
|  |                                 "--help" | ||
|  |                             ], | ||
|  |                             args: [ | ||
|  |                                 "--verbose" | ||
|  |                             ] | ||
|  |                         } | ||
|  |                     ], skipMessage); | ||
|  |                 }, | ||
|  |                 win32: async ()=>{ | ||
|  |                     const skipMessage = `This diagnostics uses system-installed tools (dumpbin.exe) to check next-swc dependencies, but it was not found in the path. Skipping dependencies check.
 | ||
|  |           dumpbin (https://learn.microsoft.com/en-us/cpp/build/reference/dumpbin-reference) is a part of Microsoft VC toolset,
 | ||
|  |           can be installed with Windows SDK, Windows Build tools or Visual Studio. | ||
|  | 
 | ||
|  |           Please make sure you have one of them installed and dumpbin.exe is in the path. | ||
|  |           `;
 | ||
|  |                     return await runSharedDependencyCheck([ | ||
|  |                         { | ||
|  |                             bin: "dumpbin.exe", | ||
|  |                             checkArgs: [ | ||
|  |                                 "/summary" | ||
|  |                             ], | ||
|  |                             args: [ | ||
|  |                                 "/imports" | ||
|  |                             ] | ||
|  |                         } | ||
|  |                     ], skipMessage); | ||
|  |                 }, | ||
|  |                 darwin: async ()=>{ | ||
|  |                     const skipMessage = "This diagnostics uses system-installed tools (otools, dyld_info) to check next-swc dependencies, but none of them are found. Skipping dependencies check."; | ||
|  |                     return await runSharedDependencyCheck([ | ||
|  |                         { | ||
|  |                             bin: "otool", | ||
|  |                             checkArgs: [ | ||
|  |                                 "--version" | ||
|  |                             ], | ||
|  |                             args: [ | ||
|  |                                 "-L" | ||
|  |                             ] | ||
|  |                         }, | ||
|  |                         { | ||
|  |                             bin: "dyld_info", | ||
|  |                             checkArgs: [], | ||
|  |                             args: [] | ||
|  |                         } | ||
|  |                     ], skipMessage); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     ]; | ||
|  |     // Collected output after running all tasks.
 | ||
|  |     const report = []; | ||
|  |     console.log("\n"); | ||
|  |     for (const task of tasks){ | ||
|  |         if (task.targetPlatform && task.targetPlatform !== currentPlatform) { | ||
|  |             report.push({ | ||
|  |                 title: task.title, | ||
|  |                 result: { | ||
|  |                     messages: undefined, | ||
|  |                     output: `[SKIPPED (${_os.default.platform()} / ${task.targetPlatform})] ${task.title}`, | ||
|  |                     result: "skipped" | ||
|  |                 } | ||
|  |             }); | ||
|  |             continue; | ||
|  |         } | ||
|  |         const taskScript = task.scripts[currentPlatform] ?? task.scripts.default; | ||
|  |         let taskResult; | ||
|  |         try { | ||
|  |             taskResult = await taskScript(); | ||
|  |         } catch (e) { | ||
|  |             taskResult = { | ||
|  |                 messages: `Unexpected failure while running diagnostics: ${e.message}`, | ||
|  |                 result: "fail" | ||
|  |             }; | ||
|  |         } | ||
|  |         console.log(`- ${task.title}: ${taskResult.result}`); | ||
|  |         if (taskResult.messages) { | ||
|  |             console.log(`  ${taskResult.messages}`); | ||
|  |         } | ||
|  |         report.push({ | ||
|  |             title: task.title, | ||
|  |             result: taskResult | ||
|  |         }); | ||
|  |     } | ||
|  |     console.log(`\n${(0, _picocolors.bold)("Generated diagnostics report")}`); | ||
|  |     console.log(`\nPlease copy below report and paste it into your issue.`); | ||
|  |     for (const { title, result } of report){ | ||
|  |         console.log(`\n### ${title}`); | ||
|  |         if (result.messages) { | ||
|  |             console.log(result.messages); | ||
|  |         } | ||
|  |         if (result.output) { | ||
|  |             console.log(result.output); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | /** | ||
|  |  * Runs few scripts to collect system information to help with debugging next.js installation issues. | ||
|  |  * There are 2 modes, by default it collects basic next.js installation with runtime information. If | ||
|  |  * `--verbose` mode is enabled it'll try to collect, verify more data for next-swc installation and others. | ||
|  |  */ const nextInfo = async (args)=>{ | ||
|  |     if (args["--help"]) { | ||
|  |         printHelp(); | ||
|  |         return; | ||
|  |     } | ||
|  |     if (!args["--verbose"]) { | ||
|  |         await printDefaultInfo(); | ||
|  |     } else { | ||
|  |         await printVerbose(); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | //# sourceMappingURL=next-info.js.map
 |