UNPKG

next

Version:

The React Framework

256 lines (255 loc) • 10.9 kB
import { bold, green, magenta, red } from '../../../lib/picocolors'; import isInternal from '../is-internal'; import { decodeMagicIdentifier, MAGIC_IDENTIFIER_REGEX } from '../magic-identifier'; import * as Log from '../../../build/output/log'; import loadJsConfig from '../../../build/load-jsconfig'; import { eventErrorThrown } from '../../../telemetry/events'; import { traceGlobals } from '../../../trace/shared'; // An error generated from emitted Turbopack issues. This can include build // errors caused by issues with user code. export class ModuleBuildError extends Error { constructor(...args){ super(...args), this.name = 'ModuleBuildError'; } } // An error caused by an internal issue in Turbopack. These should be written // to a log file and details should not be shown to the user. export class TurbopackInternalError extends Error { static createAndRecordTelemetry(cause) { const error = new TurbopackInternalError(cause); const telemetry = traceGlobals.get('telemetry'); if (telemetry) { telemetry.record(eventErrorThrown(error)); } else { console.error('Expected `telemetry` to be set in globals'); } return error; } constructor(cause){ super(cause.message), this.name = 'TurbopackInternalError', // Manually set this as this isn't statically determinable this.__NEXT_ERROR_CODE = 'TurbopackInternalError'; this.stack = cause.stack; } } /** * Thin stopgap workaround layer to mimic existing wellknown-errors-plugin in webpack's build * to emit certain type of errors into cli. */ export function isWellKnownError(issue) { const { title } = issue; const formattedTitle = renderStyledStringToErrorAnsi(title); // TODO: add more well known errors if (formattedTitle.includes('Module not found') || formattedTitle.includes('Unknown module type')) { return true; } return false; } export function getIssueKey(issue) { return issue.severity + "-" + issue.filePath + "-" + JSON.stringify(issue.title) + "-" + JSON.stringify(issue.description); } export async function getTurbopackJsConfig(dir, nextConfig) { const { jsConfig } = await loadJsConfig(dir, nextConfig); return jsConfig != null ? jsConfig : { compilerOptions: {} }; } export function processIssues(currentEntryIssues, key, result, throwIssue, logErrors) { const newIssues = new Map(); currentEntryIssues.set(key, newIssues); const relevantIssues = new Set(); for (const issue of result.issues){ if (issue.severity !== 'error' && issue.severity !== 'fatal' && issue.severity !== 'warning') continue; const issueKey = getIssueKey(issue); newIssues.set(issueKey, issue); if (issue.severity !== 'warning') { if (throwIssue) { const formatted = formatIssue(issue); relevantIssues.add(formatted); } else if (logErrors && isWellKnownError(issue)) { const formatted = formatIssue(issue); Log.error(formatted); } } } if (relevantIssues.size && throwIssue) { throw Object.defineProperty(new ModuleBuildError([ ...relevantIssues ].join('\n\n')), "__NEXT_ERROR_CODE", { value: "E394", enumerable: false, configurable: true }); } } export function formatIssue(issue) { const { filePath, title, description, source, importTraces } = issue; let { documentationLink } = issue; const formattedTitle = renderStyledStringToErrorAnsi(title).replace(/\n/g, '\n '); // TODO: Use error codes to identify these // TODO: Generalize adapting Turbopack errors to Next.js errors if (formattedTitle.includes('Module not found')) { // For compatiblity with webpack // TODO: include columns in webpack errors. documentationLink = 'https://nextjs.org/docs/messages/module-not-found'; } const formattedFilePath = filePath.replace('[project]/', './').replaceAll('/./', '/').replace('\\\\?\\', ''); let message = ''; if (source == null ? void 0 : source.range) { const { start } = source.range; message = formattedFilePath + ":" + (start.line + 1) + ":" + (start.column + 1) + "\n" + formattedTitle; } else if (formattedFilePath) { message = formattedFilePath + "\n" + formattedTitle; } else { message = formattedTitle; } message += '\n'; if ((source == null ? void 0 : source.range) && source.source.content && // ignore Next.js/React internals, as these can often be huge bundled files. !isInternal(filePath)) { const { start, end } = source.range; const { codeFrameColumns } = require('next/dist/compiled/babel/code-frame'); message += codeFrameColumns(source.source.content, { start: { line: start.line + 1, column: start.column + 1 }, end: { line: end.line + 1, column: end.column + 1 } }, { forceColor: true }).trim() + '\n\n'; } if (description) { if (description.type === 'text' && description.value.includes("Cannot find module 'sass'")) { message += "To use Next.js' built-in Sass support, you first need to install `sass`.\n"; message += 'Run `npm i sass` or `yarn add sass` inside your workspace.\n'; message += '\nLearn more: https://nextjs.org/docs/messages/install-sass'; } else { message += renderStyledStringToErrorAnsi(description) + '\n\n'; } } // TODO: make it possible to enable this for debugging, but not in tests. // if (detail) { // message += renderStyledStringToErrorAnsi(detail) + '\n\n' // } if (importTraces == null ? void 0 : importTraces.length) { // This is the same logic as in turbopack/crates/turbopack-cli-utils/src/issue.rs if (importTraces.length === 1) { const trace = importTraces[0]; // We only display the layer if there is more than one for the trace message += "Import trace:\n" + formatIssueTrace(trace, ' ', !identicalLayers(trace)); } else { // We end up with multiple traces when the file with the error is reachable from multiple // different entry points (e.g. ssr, client) message += 'Import traces:\n'; const everyTraceHasADistinctRootLayer = new Set(importTraces.map(leafLayerName).filter((l)=>l != null)).size === importTraces.length; for(let i = 0; i < importTraces.length; i++){ const trace = importTraces[i]; const layer = leafLayerName(trace); if (everyTraceHasADistinctRootLayer) { message += " " + layer + ":\n"; } else { message += " #" + (i + 1); if (layer) { message += " [" + layer + "]"; } message += ':\n'; } message += formatIssueTrace(trace, ' ', !identicalLayers(trace)); } } } if (documentationLink) { message += documentationLink + '\n\n'; } return message; } /** Returns the first present layer name in the trace */ function leafLayerName(items) { for (const item of items){ const layer = item.layer; if (layer != null) return layer; } return undefined; } /** * Returns whether or not all items share the same layer. * If a layer is absent we ignore it in this analysis */ function identicalLayers(items) { const firstPresentLayer = items.findIndex((t)=>t.layer != null); if (firstPresentLayer === -1) return true // all layers are absent ; const layer = items[firstPresentLayer].layer; for(let i = firstPresentLayer + 1; i < items.length; i++){ const itemLayer = items[i].layer; if (itemLayer == null || itemLayer !== layer) { return false; } } return true; } function formatIssueTrace(items, indent, printLayers) { return items.map((item)=>{ let r = indent; if (item.fsName !== 'project') { r += "[" + item.fsName + "]/"; } else { // This is consistent with webpack's output r += './'; } r += item.path; if (printLayers && item.layer) { r += " [" + item.layer + "]"; } return r; }).join('\n') + '\n\n'; } export function isRelevantWarning(issue) { return issue.severity === 'warning' && !isNodeModulesIssue(issue); } function isNodeModulesIssue(issue) { if (issue.severity === 'warning' && issue.stage === 'config') { // Override for the externalize issue // `Package foo (serverExternalPackages or default list) can't be external` if (renderStyledStringToErrorAnsi(issue.title).includes("can't be external")) { return false; } } return issue.severity === 'warning' && (issue.filePath.match(/^(?:.*[\\/])?node_modules(?:[\\/].*)?$/) !== null || // Ignore Next.js itself when running next directly in the monorepo where it is not inside // node_modules anyway. // TODO(mischnic) prevent matches when this is published to npm issue.filePath.startsWith('[project]/packages/next/')); } export function renderStyledStringToErrorAnsi(string) { function decodeMagicIdentifiers(str) { return str.replaceAll(MAGIC_IDENTIFIER_REGEX, (ident)=>{ try { return magenta("{" + decodeMagicIdentifier(ident) + "}"); } catch (e) { return magenta("{" + ident + " (decoding failed: " + e + ")}"); } }); } switch(string.type){ case 'text': return decodeMagicIdentifiers(string.value); case 'strong': return bold(red(decodeMagicIdentifiers(string.value))); case 'code': return green(decodeMagicIdentifiers(string.value)); case 'line': return string.value.map(renderStyledStringToErrorAnsi).join(''); case 'stack': return string.value.map(renderStyledStringToErrorAnsi).join('\n'); default: throw Object.defineProperty(new Error('Unknown StyledString type', string), "__NEXT_ERROR_CODE", { value: "E138", enumerable: false, configurable: true }); } } export function isPersistentCachingEnabled(config) { var _config_experimental; return ((_config_experimental = config.experimental) == null ? void 0 : _config_experimental.turbopackPersistentCaching) || false; } //# sourceMappingURL=utils.js.map