{"id":13459090,"url":"https://github.com/poppinss/youch","last_synced_at":"2026-04-02T16:25:36.320Z","repository":{"id":16331170,"uuid":"79725901","full_name":"poppinss/youch","owner":"poppinss","description":"Pretty print JavaScript errors on the Web and the Terminal","archived":false,"fork":false,"pushed_at":"2026-03-28T07:11:54.000Z","size":12264,"stargazers_count":1194,"open_issues_count":1,"forks_count":37,"subscribers_count":11,"default_branch":"4.x","last_synced_at":"2026-03-28T12:24:44.871Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/poppinss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":"funding.json","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"thetutlage"}},"created_at":"2017-01-22T15:29:06.000Z","updated_at":"2026-03-28T07:11:57.000Z","dependencies_parsed_at":"2023-11-28T15:28:16.133Z","dependency_job_id":"28fb624b-a666-494a-8132-5b4af00bd0fd","html_url":"https://github.com/poppinss/youch","commit_stats":{"total_commits":117,"total_committers":14,"mean_commits":8.357142857142858,"dds":"0.29059829059829057","last_synced_commit":"84f6eed516785d7b8252ac47e2e993d62885ec75"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/poppinss/youch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poppinss%2Fyouch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poppinss%2Fyouch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poppinss%2Fyouch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poppinss%2Fyouch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poppinss","download_url":"https://codeload.github.com/poppinss/youch/tar.gz/refs/heads/4.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poppinss%2Fyouch/sbom","scorecard":{"id":741228,"data":{"date":"2025-08-11","repo":{"name":"github.com/poppinss/youch","commit":"944c237dd1494d8216266896033e0cbb83407464"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.1,"checks":[{"name":"Code-Review","score":1,"reason":"Found 5/30 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":10,"reason":"18 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/checks.yml:1","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release.yml:4","Warn: no topLevel permission defined: .github/workflows/stale.yml:1","Warn: no topLevel permission defined: .github/workflows/toc.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/checks.yml/4.x?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/checks.yml/4.x?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/checks.yml/4.x?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/labels.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/labels.yml/4.x?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/labels.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/labels.yml/4.x?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/release.yml/4.x?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/release.yml/4.x?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/stale.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/stale.yml/4.x?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/toc.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/poppinss/youch/toc.yml/4.x?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/release.yml:32","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   5 third-party GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/poppinss/.github/docs/SECURITY.md:1","Info: Found linked content: github.com/poppinss/.github/docs/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/poppinss/.github/docs/SECURITY.md:1","Info: Found text in security policy: github.com/poppinss/.github/docs/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 5 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch '4.x'","Warn: branch protection not enabled for branch 'develop'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-22T17:31:35.128Z","repository_id":16331170,"created_at":"2025-08-22T17:31:35.128Z","updated_at":"2025-08-22T17:31:35.128Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31109452,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-28T15:10:22.084Z","status":"ssl_error","status_checked_at":"2026-03-28T15:09:59.994Z","response_time":79,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T09:01:03.416Z","updated_at":"2026-04-02T16:25:36.284Z","avatar_url":"https://github.com/poppinss.png","language":"TypeScript","funding_links":["https://github.com/sponsors/thetutlage"],"categories":["JavaScript","TypeScript","*.js","🛠️ Developer Tools"],"sub_categories":["Node"],"readme":"# Youch\n\n\u003e Pretty print JavaScript errors on the Web and the Terminal\n\n\u003cbr /\u003e\n\n[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url] [![Downloads Stats][npm-downloads-image]][npm-url]\n\n**Featured sponsors**\n\n\u003ctable\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\n\n\u003ca href=\"https://route4me.com/?utm_source=adonisjs.com\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/thetutlage/static/refs/heads/main/featured_sponsors/logos/route4me.jpg\" /\u003e\n\u003c/a\u003e\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003ca href=\"https://ezycourse.com/?utm_source=adonisjs.com\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/thetutlage/static/refs/heads/main/featured_sponsors/logos/ezycourse.jpg\" /\u003e\n\u003c/a\u003e\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\n\u003ctd\u003e\n\n\u003ca href=\"https://meteor.software/g6h?utm_source=adonisjs.com\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/thetutlage/static/refs/heads/main/featured_sponsors/logos/galaxy.jpg\" /\u003e\n\u003c/a\u003e\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003ca href=\"https://www.lambdatest.com/?utm_source=adonisjs.com\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/thetutlage/static/refs/heads/main/featured_sponsors/logos/lambdatest.jpg\" /\u003e\n\u003c/a\u003e\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\n\u003ctd\u003e\n\n\u003ca href=\"https://relancer.com/?utm_source=adonisjs.com\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/thetutlage/static/refs/heads/main/featured_sponsors/logos/relancer.jpg\" /\u003e\n\u003c/a\u003e\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n**Used by**\n\n\u003ctable\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\n\n\u003cimg src=\"./assets/nitro.jpg\" /\u003e\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003cimg src=\"./assets/nuxt.jpg\" /\u003e\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\n\u003ctd\u003e\n\n\u003cimg src=\"./assets/cloudflare.jpg\" /\u003e\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003cimg src=\"./assets/adonisjs.jpg\" /\u003e\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**\n\n- [What is Youch?](#what-is-youch)\n- [Usage](#usage)\n  - [Render error to HTML output](#render-error-to-html-output)\n  - [Render error to ANSI output](#render-error-to-ansi-output)\n- [Anatomy of the error page](#anatomy-of-the-error-page)\n  - [Error info](#error-info)\n  - [Stack trace](#stack-trace)\n  - [Raw output](#raw-output)\n  - [Error cause](#error-cause)\n  - [Metadata (HTML only)](#metadata-html-only)\n- [Using a custom source code loader](#using-a-custom-source-code-loader)\n- [Injecting custom styles](#injecting-custom-styles)\n- [Overriding syntax highlighter](#overriding-syntax-highlighter)\n- [Configuring code editors](#configuring-code-editors)\n  - [How do you detect the user's code editor?](#how-do-you-detect-the-users-code-editor)\n- [Contributing](#contributing)\n- [Code of Conduct](#code-of-conduct)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n![](./assets/banner.png)\n\n## What is Youch?\n\nYouch is an error-parsing library that pretty prints JavaScript errors on a web page or the terminal.\n\nAs you can see in the following screenshots, the error presented by Youch is a lot more readable and presentable in comparison to the unformatted stack trace.\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eUnformatted stack trace\u003c/strong\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cimg src=\"./assets/raw-stack-trace.png\" /\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eYouch output\u003c/strong\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cimg src=\"./assets/youch-output.jpg\" /\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003ctable\u003e\n\n## Usage\n\nInstall the package from the npm packages registry as follows.\n\n```sh\nnpm i youch@beta\n\n# Yarn\nyarn add youch@beta\n\n# Pnpm\npnpm add youch@beta\n```\n\n### Render error to HTML output\n\nYou can render errors to HTML output using the `youch.toHTML` method. The HTML output is self-contained and does not require separate CSS or JavaScript files.\n\nIn the following example, we use the `hono` framework and pretty print all the errors in development using Youch. You can replace Hono with any other framework of your choice.\n\n```ts\nimport { Hono } from 'hono'\nimport { Youch } from 'youch'\n\nconst app = new Hono()\nconst IN_DEV = process.env.NODE_ENV === 'development'\n\napp.onError(async (error, c) =\u003e {\n  if (IN_DEV) {\n    const youch = new Youch()\n    const html = await youch.toHTML(error)\n    return c.html(html)\n  }\n  return c.text(error.message)\n})\n```\n\nThe `youch.toHTML` method accepts the error as the first argument and the following options as the second argument.\n\n```ts\nawait youch.toHTML(error, {\n  title: 'An error occurred',\n  cspNonce: '',\n  offset: 0,\n  ide: 'vscode',\n})\n```\n\n| Option     | Description                                                                                                                                                                    |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `title`    | Define the title for the error page. It defaults to **\"An error has occurred\"**                                                                                                |\n| `cspNonce` | If your application is using CSP protection, then you must provide the [CSP-nonce](https://content-security-policy.com/nonce/) for rendering inline `style` and `script` tags. |\n| `offset`   | The offset can be used to skip displaying certain frames from the parsed error stack.                                                                                          |\n| `ide`      | The `ide` option defines the code editor for opening the files when the filename anchor tag is clicked. [Learn more about configuring code editors](#configuring-code-editors) |\n\n### Render error to ANSI output\n\nYou can render an error to ANSI output (for terminal) using the `youch.toANSI` method.\n\n```ts\ntry {\n  await performSomeAction()\n} catch (error) {\n  const youch = new Youch()\n  const ansiOutput = await youch.toANSI(error)\n\n  console.error(ansiOutput)\n}\n```\n\nThe `youch.toANSI` method accepts the error as the first argument and the following options as the second argument.\n\n```ts\nawait youch.toANSI(error, {\n  offset: 0,\n})\n```\n\n| Option   | Description                                                                           |\n| -------- | ------------------------------------------------------------------------------------- |\n| `offset` | The offset can be used to skip displaying certain frames from the parsed error stack. |\n\n## Anatomy of the error page\n\nLet's deconstruct the error page and understand what each section of the output represents.\n\n### Error info\n\nThe top-most section displays the Error info, which includes:\n\n- The Error class constructor name.\n- The Error title. It is set using the `options.title` property.\n- And the Error message (highlighted in red).\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView HTML output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/error-info.png)\n\n\u003c/details\u003e\n\n---\n\n### Stack trace\n\nThe Stack trace section displays individual frames as accordion sections. Clicking on the section title reveals the frame source code. The source code is unavailable for native stack frames that are part of the Node.js, Deno, and Bun internals.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView HTML output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/error-stack.png)\n\n\u003c/details\u003e\n\n---\n\nFor the ANSI output, only the first frame from the application code is expanded to show the source code.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView ANSI output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/terminal-error.png)\n\n\u003c/details\u003e\n\n---\n\n### Raw output\n\nClicking the `Raw` button displays the Error object in its raw form, with all the error properties (not just the stack trace).\n\nThe raw output may be helpful for errors that contain additional properties. HTTP client libraries like Axios, Got, Undici, and others usually contain the HTTP response details within the error object.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView HTML output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/stack-raw-output.png)\n\n\u003c/details\u003e\n\n---\n\nIn case of ANSI output, you can view the raw output using the `YOUCH_RAW` environment variable. For example: `YOUCH_RAW=true node your-script.js`.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView ANSI output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/terminal-error-raw.png)\n\n\u003c/details\u003e\n\n---\n\n### Error cause\n\n[Error cause](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) is a standard way to bubble errors while wrapping them within a generic error. Youch displays the error cause as an interactive property within its own section.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView HTML output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/error-cause.png)\n\n\u003c/details\u003e\n\n---\n\nFor the ANSI output, the nested properties are shown upto the two levels deep. However, you can adjust the depth using the `YOUCH_CAUSE` environment variable. For example: `YOUCH_CAUSE=4 node your-script.js`.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eView ANSI output\u003c/strong\u003e\u003c/summary\u003e\n  \n  ![](./assets/terminal-error-cause.png)\n\n\u003c/details\u003e\n\n---\n\n### Metadata (HTML only)\n\nMetadata refers to any additional data that you want to display on the error page. It could be the HTTP request headers, the logged-in user info, or the list of available application routes.\n\nMetadata is structured as groups and sections. Each section contains an array of rows, and each row is composed of a `key-value` pair.\n\nIn the following example, we display the request headers under the `Request` group and the `Headers` section.\n\n```ts\nconst youch = new Youch()\n\nyouch.group('Request', {\n  headers: [\n    {\n      key: 'cookie',\n      value: req.headers.cookie,\n    },\n    {\n      key: 'host',\n      value: req.headers.host,\n    },\n  ],\n})\n```\n\nCalling the `youch.group` method multiple times with the same group name will merge the new sections with existing sections.\n\n## Using a custom source code loader\n\nYouch reads the source code of files within the stack trace using the Node.js `fs` module. However, you can override this default and provide a custom source loader using the `youch.sourceLoader` method.\n\n\u003e Note: The `sourceLoader` is called for every frame within the stack traces. Therefore, you must perform the necessary checks before attempting to read the source code of a file.\n\u003e\n\u003e For example, you must not attempt to read the source code for fileNames pointing to native code.\n\n```ts\nimport { Youch } from 'youch'\nconst youch = new Youch(options)\n\nyouch.sourceLoader(async (stackFrame) =\u003e {\n  if (stackFrame.type !== 'native') {\n    stackFrame.source = await getSourceForFile(stackFrame.fileName)\n  }\n})\n```\n\n## Injecting custom styles\n\nYou may inject custom CSS styles using the `youch.templates.injectStyles` method. The styles will be injected after the styles from the inbuilt templates.\n\n```ts\nimport { Youch } from 'youch'\nconst youch = new Youch(options)\n\nyouch.templates.injectStyles(`\n  :root {\n    // Override variables for light mode\n    --surface-bg: #fff;\n    --surface-fg: #000;\n    --muted-fg: #999;\n  }\n\n  html.dark {\n    // Override variables for dark mode\n  }\n`)\n```\n\n## Overriding syntax highlighter\n\nYouch uses [speed-highlight](https://github.com/speed-highlight/core), a lightweight code highlighting library for JavaScript. To override the syntax highlighter, you can register a custom component for the `errorStackSource` template.\n\nIn the following example, we use [Shiki](https://shiki.matsu.io/) to perform syntax highlighting using a custom component.\n\n```ts\nimport { codeToHtml } from 'shiki'\nimport { ErrorStackSourceProps } from 'youch/types'\nimport { ErrorStackSource } from 'youch/templates/error_stack_source'\n\n/**\n * A custom component that uses shiki to render the source\n * code snippet for a stack frame\n */\nclass CustomErrorStackSource\u003cErrorStackSourceProps\u003e extends ErrorStackSource {\n  /**\n   * Return the styles you want to inject from this\n   * component\n   */\n  async getStyles() {\n    return `\n    html.dark .shiki {\n      background-color: var(--shiki-dark-bg) !important;\n    }\n\n    html.dark .shiki span {\n      color: var(--shiki-dark) !important;\n      font-weight: var(--shiki-dark-font-weight) !important;\n      text-decoration: var(--shiki-dark-text-decoration) !important;\n    }\n\n    pre.shiki {\n      padding: 16px 0;\n    }\n\n    code .line {\n      position: relative;\n      padding: 0 16px 0 48px;\n      height: 24px;\n      line-height: 24px;\n      width: 100%;\n      display: inline-block;\n    }\n\n    code .line:before {\n      position: absolute;\n      content: attr(data-line);\n      opacity: 0.5;\n      text-align: right;\n      margin-right: 5px;\n      left: 0;\n      width: 32px;\n    }\n\n    code .highlighted {\n      background-color: #ff000632;\n    }\n\n    html.dark code .highlighted {\n      background-color: #ff173f2d !important;\n    }`\n  }\n\n  async toHTML(props: ErrorStackSourceProps) {\n    if (props.frame.source) {\n      const code = props.frame.source.map(({ chunk }) =\u003e chunk).join('\\n')\n\n      return codeToHtml(code, {\n        lang: 'typescript',\n        themes: {\n          light: 'min-light',\n          dark: 'vitesse-dark',\n        },\n        transformers: [\n          {\n            line(node, line) {\n              const lineNumber = props.frame.source![line - 1].lineNumber\n              node.properties['data-line'] = lineNumber\n\n              if (lineNumber === props.frame.lineNumber) {\n                this.addClassToHast(node, 'highlighted')\n              }\n            },\n          },\n        ],\n      })\n    }\n\n    return ''\n  }\n}\n\nconst youch = new Youch()\n\n/**\n * Register the component\n */\nyouch.templates.use('errorStackSource', new CustomErrorStackSource(false))\n\nconst html = await youch.toHTML(error)\n```\n\n## Configuring code editors\n\nWhen you click the filename anchor tag (displayed in the pretty error stack section), Youch will attempt to open the given file inside a pre-configured code editor (defaults to `vscode`).\n\nYou can specify which code editor to use via the `ide` option. Following is the list of support code editors.\n\n- textmate\n- macvim\n- emacs\n- sublime\n- phpstorm\n- atom\n- vscode\n\nIf you prefer a different code editor, specify its URL via the `ide` option. Make sure the URL contains the `%f` placeholder for the filename and the `%l` placeholder for the line number.\n\n```ts\nawait youch.toHTML(error, {\n  ide: 'mvim://open?url=file://%f\u0026line=%l',\n})\n```\n\n### How do you detect the user's code editor?\n\nYouch relies on the `process.env.IDE` environment variable to detect the user's code editor and falls back to `vscode` if the environment variable is not defined.\n\nHowever, you can use any detection logic and specify the detected code editor via the `ide` option. For example, In the case of AdonisJS, we configure the code editor within the `.env` file using the `ADONIS_IDE` environment variable.\n\n## Contributing\n\nOne of the primary goals of Poppinss is to have a vibrant community of users and contributors who believe in the principles of the framework.\n\nWe encourage you to read the [contribution guide](https://github.com/poppinss/.github/blob/main/docs/CONTRIBUTING.md) before contributing to the framework.\n\n## Code of Conduct\n\nIn order to ensure that the Poppinss community is welcoming to all, please review and abide by the [Code of Conduct](https://github.com/poppinss/.github/blob/main/docs/CODE_OF_CONDUCT.md).\n\n## License\n\nYouch is open-sourced software licensed under the [MIT license](LICENSE.md).\n\n[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/poppinss/youch/checks.yml?style=for-the-badge\n[gh-workflow-url]: https://github.com/poppinss/youch/actions/workflows/checks.yml 'Github action'\n[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge\u0026logo=typescript\n[typescript-url]: \"typescript\"\n[npm-image]: https://img.shields.io/npm/v/youch.svg?style=for-the-badge\u0026logo=npm\n[npm-url]: https://npmjs.org/package/youch 'npm'\n[license-image]: https://img.shields.io/npm/l/youch?color=blueviolet\u0026style=for-the-badge\n[license-url]: LICENSE.md 'license'\n[npm-downloads-image]: https://img.shields.io/npm/dm/youch.svg?style=for-the-badge\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoppinss%2Fyouch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoppinss%2Fyouch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoppinss%2Fyouch/lists"}