vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
197 lines (196 loc) • 6.67 kB
JavaScript
export { logErrorHint };
// For ./logErrorHint/getErrorHint.spec.ts
export { getErrorHint };
import { assert, formatHintLog, isObject } from '../utils.js';
import pc from '@brillout/picocolors';
const hintDefault = 'The error could be a CJS/ESM issue, see https://vike.dev/broken-npm-package';
const hintLinkPrefix = 'To fix this error, see ';
const errorsMisc = [
{
errMsg: 'window is not defined',
link: 'https://vike.dev/hints#window-is-not-defined',
mustMentionNodeModules: false,
},
{
errMsg: 'jsxDEV is not a function',
link: 'https://github.com/vikejs/vike/issues/1469#issuecomment-1919518096',
mustMentionNodeModules: false,
},
{
// ```
// Error [RollupError]: Could not resolve "../dist/client/assets.json" from "renderer/+onRenderHtml.tsx"
// ```
errMsg: 'assets.json',
link: 'https://vike.dev/getGlobalContext',
mustMentionNodeModules: false,
},
{
errMsg: 'ERR_UNKNOWN_FILE_EXTENSION',
link: 'https://vike.dev/broken-npm-package#err-unknown-file-extension',
},
];
const reactInvalidEelement = 'https://vike.dev/broken-npm-package#react-invalid-component';
const errorsReact = [
{
errMsg: 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components)',
link: reactInvalidEelement,
// The stack trace can be user-land while the import is coming from node_modules
mustMentionNodeModules: false,
},
{
errMsg: 'Objects are not valid as a React child',
link: reactInvalidEelement,
mustMentionNodeModules: false,
},
{
// React's "Invalid hook call.", see https://github.com/vikejs/vike/discussions/1637#discussioncomment-9424712
errMsg: "Cannot read properties of null (reading 'useContext')",
},
];
const errorsCjsEsm_withPreciseLink = [
{
// `SyntaxError: Named export '${exportName}' not found. The requested module '${packageName}' is a CommonJS module, which may not support all module.exports as named exports.`
errMsg: /Named export.*not found/i,
link: 'https://vike.dev/broken-npm-package#named-export-not-found',
// It seems that this always points to an npm package import.
mustMentionNodeModules: false,
},
];
const errorsCjsEsm = [
{ errMsg: 'ERR_UNSUPPORTED_DIR_IMPORT' },
{ errMsg: 'ERR_REQUIRE_ESM' },
{ errMsg: 'Must use import' },
{ errMsg: /Cannot find \S+ '(\S+)' imported from (\S+)/ },
{ errMsg: 'ERR_UNKNOWN_FILE_EXTENSION' },
{ errMsg: /Unknown file extension "\S+" for (\S+)/ },
// `SyntaxError: Cannot use import statement outside a module`.
{
errMsg: 'Cannot use import statement',
// Since user code is always ESM, this error must always originate from an npm package.
mustMentionNodeModules: false,
},
{ errMsg: 'is not exported' },
{ errMsg: 'Cannot read properties of undefined' },
{ errMsg: '.default is not' },
// Using CJS inside ESM modules.
{ errMsg: 'require is not a function' },
{ errMsg: 'exports is not defined' },
{ errMsg: 'module is not defined' },
{ errMsg: 'not defined in ES' },
{ errMsg: "Unexpected token 'export'" },
{ errMsg: 'Failed to resolve entry for package' },
];
function logErrorHint(error) {
/* Collect errors for ./logErrorHint.spec.ts
collectError(error)
//*/
const hint = getErrorHint(error);
if (hint)
logHint(hint);
}
function getErrorHint(error) {
{
const knownErr = isKnownError(error);
if (knownErr) {
if (knownErr.link) {
return hintLinkPrefix + knownErr.link;
}
else {
return hintDefault;
}
}
}
return null;
}
function logHint(hint) {
hint = formatHintLog(hint);
hint = pc.bold(hint);
console.error(hint);
}
function isKnownError(error) {
const anywhere = getAnywhere(error);
const knownErr = [
//
...errorsMisc,
...errorsReact,
...errorsCjsEsm_withPreciseLink,
...errorsCjsEsm,
].find((knownError) => {
if (!includesLowercase(anywhere, knownError.errMsg))
return false;
if (knownError.mustMentionNodeModules !== false && !includesLowercase(anywhere, 'node_modules'))
return false;
return true;
});
if (!knownErr)
return false;
return knownErr;
}
function includesLowercase(str, substr) {
if (substr instanceof RegExp) {
let { flags } = substr;
if (!flags.includes('i'))
flags += 'i';
const regex = new RegExp(substr.source, flags);
return regex.test(str);
}
if (typeof substr === 'string') {
return str.toLowerCase().includes(substr.toLowerCase());
}
assert(false);
}
function getAnywhere(error) {
const code = getErrCode(error);
const message = getErrMessage(error);
const stack = getErrStack(error);
const anywhere = [code, message, stack].filter(Boolean).join('\n');
return anywhere;
}
function getErrMessage(err) {
if (!isObject(err))
return null;
if (!err.message)
return null;
if (typeof err.message !== 'string')
return null;
return err.message;
}
function getErrCode(err) {
if (!isObject(err))
return null;
if (!err.code)
return null;
if (typeof err.code !== 'string')
return null;
return err.code;
}
function getErrStack(err) {
if (!isObject(err))
return null;
if (!err.stack)
return null;
if (typeof err.stack !== 'string')
return null;
return err.stack;
}
function collectError(err) {
console.log([
'{',
` message: ${JSON.stringify(err.message)},`,
` code: ${JSON.stringify(err.code)},`,
' stack: `\n' + err.stack + '\n`',
'}',
].join('\n'));
/* For reproductions using older vite-plugin-ssr versions, do one of the following.
- If upon pre-rendering:https: //github.com/brillout/repro_node-syntax-error#error-catched-by-vite-plugin-ssr
- Inject the logger inside `catch` in node_modules/vite-plugin-ssr/dist/esm/node/runtime/renderPage.js
- Inject the following inside `configResolved(config_)` at node_modules/vite-plugin-ssr/dist/cjs/node/vite/plugins/pluginDev/index.js
```js
config_.logger.error = (msg, options) => {
const { error } = options;
if (error) return;
console.log(...);
};
```
*/
}