[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [yoavbls, kevinramharak]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"\"\nlabels: bug\nassignees: \"\"\n---\n\n### **Describe the bug**\n\nA clear and concise description of what the bug is.\n\n### **Expected behavior**\n\nA clear and concise description of what you expected to happen.\n\n### **Original error**\n\nIf this bug is related to an error that is not formatting well, please\nattach the original error in a code block:\n\n```\nType 'number' is not assignable to type 'string'.ts(2322)\n```\n\n### **Logs**\n\nAdd the logs to help debugging what went wrong. See [these instructions](https://github.com/yoavbls/pretty-ts-errors/blob/main/docs/vscode-logs.md) on how to find and export the logs.\n\nEither add it as an external file or put them in between these `<pre><code>` tags below:\n\n<details>\n<summary>Logs</summary>\n<pre><code>\n<!-- replace this comment with your log output -->\n</code></pre>\n</details>\n\n### **Screenshots**\n\nIf applicable, add screenshots to help explain your problem.\n"
  },
  {
    "path": ".github/workflows/pr-ci.yml",
    "content": "name: PR CI\n\non:\n  workflow_dispatch:\n  pull_request:\n    types: [opened, synchronize, reopened, ready_for_review]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: pr-ci-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\njobs:\n  validate:\n    name: Install, Build, and Test\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: npm\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Install VSCE CLI\n        run: npm install -g @vscode/vsce\n\n      - name: Check formatting\n        run: npm run format:check\n\n      - name: Build all workspaces\n        run: npm run build\n\n      - name: Run formatter tests\n        run: npm -w @pretty-ts-errors/formatter run test -- --reporter=verbose\n\n      - name: Run VS Code formatter tests\n        run: npm -w @pretty-ts-errors/vscode-formatter run test -- --reporter=verbose\n\n      - name: Run VS Code extension tests\n        run: xvfb-run -a npm --workspace apps/vscode-extension run test\n"
  },
  {
    "path": ".gitignore",
    "content": "out\ndist\nnode_modules\n.vscode-test/\n*.vsix\n.turbo\n.vercel\n.idea\n**/.DS_Store\ntsconfig.tsbuildinfo"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"trailingComma\": \"es5\"\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  // See http://go.microsoft.com/fwlink/?LinkId=827846\n  // for the documentation about the extensions.json format\n  \"recommendations\": [\n    \"dbaeumer.vscode-eslint\",\n    \"connor4312.esbuild-problem-matchers\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "// A launch configuration that compiles the extension and then opens it inside a new window\n// Use IntelliSense to learn about possible attributes.\n// Hover to view descriptions of existing attributes.\n// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Run Extension\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"args\": [\n        \"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode-extension\"\n      ],\n      \"outFiles\": [\n        \"${workspaceFolder}/apps/vscode-extension/dist/**/*.js\",\n        \"${workspaceFolder}/apps/vscode-extension/out/**/*.js\"\n      ],\n      \"preLaunchTask\": \"watch - apps/vscode-extension\"\n    },\n    {\n      \"name\": \"Run Extension (all extensions disabled)\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"args\": [\n        \"--disable-extensions\",\n        \"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode-extension\"\n      ],\n      \"outFiles\": [\n        \"${workspaceFolder}/apps/vscode-extension/dist/**/*.js\",\n        \"${workspaceFolder}/apps/vscode-extension/out/**/*.js\"\n      ],\n      \"preLaunchTask\": \"watch - apps/vscode-extension\"\n    },\n    {\n      \"name\": \"Extension Tests\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"args\": [\n        \"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode-extension\",\n        \"--extensionTestsPath=${workspaceFolder}/apps/vscode-extension/out/test/suite/index\"\n      ],\n      \"outFiles\": [\n        \"${workspaceFolder}/apps/vscode-extension/out/**/*.js\",\n        \"${workspaceFolder}/apps/vscode-extension/dist/**/*.js\"\n      ],\n      \"preLaunchTask\": \"tasks: watch-tests\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"files.exclude\": {\n    \"out\": false, // set this to true to hide the \"out\" folder with the compiled JS files\n    \"dist\": false // set this to true to hide the \"dist\" folder with the compiled JS files\n  },\n  \"search.exclude\": {\n    \"out\": true, // set this to false to include \"out\" folder in search results\n    \"dist\": true // set this to false to include \"dist\" folder in search results\n  },\n  // Turn off tsc task auto detection since we have the necessary tasks as npm scripts\n  \"typescript.tsc.autoDetect\": \"off\",\n  \"chat.tools.terminal.autoApprove\": {\n    \"npx vitest\": true\n  }\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "// See https://go.microsoft.com/fwlink/?LinkId=733558\n// for the documentation about the tasks.json format\n{\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"label\": \"watch - apps/vscode-extension\",\n      \"type\": \"shell\",\n      \"command\": \"npm run watch --silent\",\n      \"options\": {\n        \"cwd\": \"${workspaceFolder}/apps/vscode-extension\"\n      },\n      \"problemMatcher\": {\n        \"owner\": \"esbuild\",\n        \"fileLocation\": \"autoDetect\",\n        \"pattern\": [\n          {\n            \"regexp\": \"^✘ \\\\[ERROR\\\\] (.*)$\",\n            \"message\": 1\n          },\n          {\n            \"regexp\": \"^\\\\s*(.*):(\\\\d+):(\\\\d+):$\",\n            \"file\": 1,\n            \"line\": 2,\n            \"column\": 3\n          }\n        ],\n        \"background\": {\n          \"activeOnStart\": true,\n          \"beginsPattern\": \"^\\\\[watch\\\\] build started$\",\n          \"endsPattern\": \"^\\\\[watch\\\\] build finished$\"\n        }\n      },\n      \"isBackground\": true,\n      \"presentation\": {\n        \"reveal\": \"never\",\n        \"group\": \"watchers\"\n      },\n      \"group\": {\n        \"kind\": \"build\",\n        \"isDefault\": true\n      }\n    },\n    {\n      \"type\": \"npm\",\n      \"script\": \"watch-tests\",\n      \"path\": \"apps/vscode-extension\",\n      \"problemMatcher\": \"$tsc-watch\",\n      \"isBackground\": true,\n      \"presentation\": {\n        \"reveal\": \"never\",\n        \"group\": \"watchers\"\n      },\n      \"group\": \"build\"\n    },\n    {\n      \"label\": \"tasks: watch-tests\",\n      \"dependsOn\": [\"watch - apps/vscode-extension\", \"npm: watch-tests\"],\n      \"problemMatcher\": []\n    }\n  ]\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contribute good stuff\n\nIf you're looking for ideas, check out our [board](https://github.com/users/yoavbls/projects/3) and [open issues](https://github.com/yoavbls/pretty-ts-errors/issues)\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Yoav Balasiano\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors\" style=\"display: none;\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/icon.png\" width=\"140\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/icon.png\" width=\"140\">\n    <img src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/empty.png\" alt=\"Logo\">\n  </picture>\n</a>\n\n# Pretty `TypeScript` Errors\n\n<b>Make TypeScript errors prettier and human-readable in VSCode.</b>\n\n[![GitHub stars](https://img.shields.io/github/stars/yoavbls/pretty-ts-errors.svg?style=social&label=Star)](https://GitHub.com/yoavbls/pretty-ts-errors/stargazers/)\n[![Visual Studio Code](https://img.shields.io/badge/Visual%20Studio%20Code-0078d7?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors)&nbsp;[![GitHub license](https://badgen.net/github/license/yoavbls/pretty-ts-errors)](https://github.com/yoavbls/pretty-ts-errors/blob/main/LICENSE)&nbsp;[![Visual Studio Code](https://img.shields.io/visual-studio-marketplace/i/yoavbls.pretty-ts-errors)](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors)\n<a href=\"https://github.com/yoavbls/pretty-ts-errors/discussions/43#user-content-jetbrains-support\"><img src=\"https://cdn.icon-icons.com/icons2/2530/PNG/512/jetbrains_webstorm_button_icon_151873.png\" height=\"20\" alt=\"Webstorm logo\"></a>\n[![Cursor](https://img.shields.io/badge/Cursor-000000?logo=cursor)](https://open-vsx.org/extension/yoavbls/pretty-ts-errors)\n\nTypeScript errors become messier as the complexity of types increases. At some point, TypeScript will throw on you a shitty heap of parentheses and `\"...\"`.\nThis extension will help you understand what's going on. For example, in this relatively simple error:\n\n<img src=\"./assets/this.png\" width=\"340.438px\" />&nbsp; &nbsp; <img src=\"./assets/instead-of-that.png\" width=\"350px\" />\n\n## Watch this\n\n<a href=\"https://www.youtube.com/watch?v=9RM2aErJs-s\" target=\"_blank\">\n <img src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/theo-video.png\" alt=\"Watch theo's video\" width=\"600\" />\n</a>\n\nand others from:\n[Web Dev Simplified](https://www.youtube.com/watch?v=ccg-erZYO4k&list=PL0rc4JAdEsVpOriHzlAG7KUnhKIK9c7OR&index=1),\n[Josh tried coding](https://www.youtube.com/watch?v=_9y29Cyo9uU&list=PL0rc4JAdEsVpOriHzlAG7KUnhKIK9c7OR&index=3),\n[trash dev](https://www.youtube.com/watch?v=WJeD3DKlWT4&list=PL0rc4JAdEsVpOriHzlAG7KUnhKIK9c7OR&index=4&t=208),\nand [more](https://www.youtube.com/playlist?list=PL0rc4JAdEsVpOriHzlAG7KUnhKIK9c7OR)\n\n## Features\n\n- Syntax highlighting with your theme colors for types in error messages, supporting both light and dark themes\n- A button that leads you to the relevant type declaration next to the type in the error message\n- A button that navigates you to the error at [typescript.tv](http://typescript.tv), where you can find a detailed explanation, sometimes with a video\n- A button that navigates you to [ts-error-translator](https://ts-error-translator.vercel.app/), where you can read the error in plain English\n\n## Supports\n\n- Node and Deno TypeScript error reporters (in `.ts` files)\n- JSDoc type errors (in `.js` and `.jsx` files)\n- React, Solid and Qwik errors (in `.tsx` and `.mdx` files)\n- Astro, Svelte and Vue files when TypeScript is enabled (in `.astro`, `.svelte` and `.vue` files)\n- Ember and Glimmer TypeScript errors and template issues reported by Glint (in `.hbs`, `.gjs`, and `.gts` files)\n\n## Installation\n\n```\ncode --install-extension yoavbls.pretty-ts-errors\n```\n\nOr simply by searching for `pretty-ts-errors` in the [VSCode marketplace](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors)\n\n#### How to hide the original errors and make the types copyable\n\nFollow the instructions [there](./docs/hide-original-errors.md). unfortunately, this hack is required because of VSCode limitations.\n\n## Why isn't it trivial\n\n1. TypeScript errors contain types that are not valid in TypeScript.\n   Yes, these types include things like `... more ...`, `{ ... }`, etc in an inconsistent manner. Some are also cutting in the middle because they're too long.\n2. Types can't be syntax highlighted in code blocks because the part of `type X = ...` is missing, so I needed to create a new TextMate grammar, a superset of TypeScript grammar called `type`.\n3. VSCode markdown blocks all styling options, so I had to find hacks to style the error messages. e.g., there isn't an inlined code block on VSCode markdown, so I used a code block inside a codicon icon, which is the only thing that can be inlined. That's why it can't be copied. but it isn't a problem because you can still hover on the error and copy things from the original error pane.\n   <img src=\"./assets/errors-hover.png\" width=\"600\" />\n\n## Hype section\n\n<a href=\"https://www.youtube.com/live/Zze1y2iZ3bQ?si=Yj1Qw2S8FbGbTA5c&t=11589\">\n  <picture>\n    <img width=\"400\" src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/js-nation.png?raw=true\" alt=\"Winning the Productivity Booster category at JSNation 2023\">\n  </picture>\n</a>\n<a href=\"https://twitter.com/tannerlinsley/status/1647982562026090496\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/tanner-dark.png#gh-light-mode-only\">\n     <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/tanner-light.png#gh-light-mode-only\">\n    <img width=\"400\" src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/tanner-dark.png#gh-dark-mode-only\" alt=\"Tanner's tweet\">\n  </picture>\n</a>\n<a href=\"https://twitter.com/t3dotgg/status/1647759462709747713\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/theo-dark.png#gh-dark-mode-only\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/theo-light.png#gh-light-mode-only\">\n    <img width=\"400\" src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/theo-dark.png#gh-dark-mode-only\" alt=\"Theo's tweet\">\n  </picture>\n</a>\n<a href=\"https://twitter.com/johnsoncodehk/status/1646214711204286465\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/johnson-dark.png#gh-dark-mode-only\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/johnson-light.png#gh-light-mode-only\">\n    <img width=\"400\" src=\"https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/main/assets/mentions/johnson-dark.png#gh-dark-mode-only\" alt=\"Johnson's tweet\">\n  </picture>\n</a>\n\n### Stars from stars\n\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\">\n        <a href=\"https://github.com/kentcdodds\">\n          <img src=\"https://images.weserv.nl/?url=github.com/kentcdodds.png&fit=cover&mask=circle\" width=\"80\"><br>\n          Kent C. Dodds\n        <a/>\n      </td>\n      <td align=\"center\">\n        <a href=\"https://github.com/mattpocock\">\n          <img src=\"https://images.weserv.nl/?url=github.com/mattpocock.png&fit=cover&mask=circle\" width=\"80\"><br>\n          Matt Pocock\n        <a/>\n      </td>\n      <td align=\"center\">\n        <a href=\"https://github.com/katt\">\n          <img src=\"https://images.weserv.nl/?url=github.com/katt.png&fit=cover&mask=circle\" width=\"80\"><br>\n          Alex / KATT\n        <a/>\n      </td>\n      <td align=\"center\">\n        <a href=\"https://github.com/tannerlinsley\">\n          <img src=\"https://images.weserv.nl/?url=github.com/tannerlinsley.png&fit=cover&mask=circle\" width=\"80\"><br>\n          Tanner Linsley\n        <a/>\n      </td>\n      <td align=\"center\">\n        <a href=\"https://github.com/t3dotgg\">\n          <img src=\"https://images.weserv.nl/?url=github.com/t3dotgg.png&fit=cover&mask=circle\" width=\"80\"><br>\n          Theo Browne\n        <a/>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n## Sponsorship\n\nEvery penny will be invested in other contributors to the project, especially ones that work\non things that I can't be doing myself like adding support to the extension for other IDEs 🫂\n\n## Contribution\n\nHelp by upvoting or commenting on issues we need to be resolved [here](https://github.com/yoavbls/pretty-ts-errors/discussions/43)\nAny other contribution is welcome. Feel free to open any issue / PR you think.\n"
  },
  {
    "path": "apps/vscode-extension/.gitignore",
    "content": "README.md"
  },
  {
    "path": "apps/vscode-extension/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Yoav Balasiano\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "apps/vscode-extension/package.json",
    "content": "{\n  \"name\": \"pretty-ts-errors\",\n  \"displayName\": \"Pretty TypeScript Errors\",\n  \"publisher\": \"YoavBls\",\n  \"description\": \"Make TypeScript errors prettier and more human-readable in VSCode\",\n  \"version\": \"0.8.4\",\n  \"icon\": \"assets/icon.png\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/yoavbls/pretty-ts-errors\",\n    \"directory\": \"apps/vscode-extension\"\n  },\n  \"homepage\": \"https://github.com/yoavbls/pretty-ts-errors\",\n  \"engines\": {\n    \"vscode\": \"^1.77.0\"\n  },\n  \"vsce\": {\n    \"dependencies\": false\n  },\n  \"categories\": [\n    \"Programming Languages\",\n    \"Debuggers\",\n    \"Visualization\",\n    \"Other\"\n  ],\n  \"galleryBanner\": {\n    \"color\": \"#133b55\",\n    \"theme\": \"dark\"\n  },\n  \"activationEvents\": [\n    \"onLanguage:typescript\",\n    \"onLanguage:javascript\",\n    \"onLanguage:typescriptreact\",\n    \"onLanguage:javascriptreact\",\n    \"onLanguage:astro\",\n    \"onLanguage:svelte\",\n    \"onLanguage:vue\",\n    \"onLanguage:mdx\",\n    \"onLanguage:glimmer-js\",\n    \"onLanguage:glimmer-ts\"\n  ],\n  \"main\": \"./dist/extension.js\",\n  \"browser\": \"./dist/extension.js\",\n  \"files\": [\n    \"dist/**/*\",\n    \"assets/**/*\",\n    \"syntaxes/**/*\",\n    \"webview/**/*\",\n    \"LICENSE\"\n  ],\n  \"contributes\": {\n    \"viewsContainers\": {\n      \"activitybar\": [\n        {\n          \"id\": \"prettyTsErrors\",\n          \"title\": \"Pretty TypeScript Errors\",\n          \"icon\": \"./assets/ribbon.svg\"\n        }\n      ]\n    },\n    \"views\": {\n      \"prettyTsErrors\": [\n        {\n          \"id\": \"prettyTsErrors.sidePanel\",\n          \"icon\": \"./assets/ribbon.svg\",\n          \"name\": \"Side Panel\",\n          \"type\": \"webview\"\n        }\n      ]\n    },\n    \"commands\": [\n      {\n        \"command\": \"prettyTsErrors.revealSelection\",\n        \"title\": \"Reveal the given selection\",\n        \"category\": \"Pretty TS Errors\",\n        \"enablement\": \"!isCommandPanel\"\n      },\n      {\n        \"command\": \"prettyTsErrors.showErrorInSidebar\",\n        \"title\": \"Show Error in Sidebar\",\n        \"category\": \"Pretty TS Errors\",\n        \"icon\": \"./assets/ribbon.svg\",\n        \"enablement\": \"!isCommandPanel\"\n      },\n      {\n        \"command\": \"prettyTsErrors.pinError\",\n        \"title\": \"Pin Error\",\n        \"category\": \"Pretty TS Errors\",\n        \"enablement\": \"!isCommandPanel\"\n      },\n      {\n        \"command\": \"prettyTsErrors.unpinError\",\n        \"title\": \"Unpin Error\",\n        \"category\": \"Pretty TS Errors\",\n        \"enablement\": \"!isCommandPanel\"\n      }\n    ],\n    \"menus\": {\n      \"editor/title\": [\n        {\n          \"command\": \"prettyTsErrors.showErrorInSidebar\",\n          \"when\": \"prettyTsErrors.hasErrors && resourceLangId in prettyTsErrors.supportedLanguageIds\",\n          \"group\": \"navigation\"\n        }\n      ]\n    },\n    \"languages\": [\n      {\n        \"id\": \"type\",\n        \"extensions\": [\n          \".type\"\n        ]\n      }\n    ],\n    \"grammars\": [\n      {\n        \"language\": \"type\",\n        \"scopeName\": \"source.type\",\n        \"path\": \"./syntaxes/type.tmGrammar.json\"\n      }\n    ]\n  },\n  \"scripts\": {\n    \"vscode:prepublish\": \"cp ../../README.md README.md && npm run package\",\n    \"compile\": \"node scripts/build\",\n    \"watch\": \"npm run compile -- --watch\",\n    \"dev\": \"npm-run-all --parallel _dev:*\",\n    \"_dev:ext\": \"npm run watch\",\n    \"_dev:formatter\": \"npm run -w @pretty-ts-errors/formatter dev\",\n    \"_dev:vscode-formatter\": \"npm run -w @pretty-ts-errors/vscode-formatter dev\",\n    \"build\": \"vsce package\",\n    \"package\": \"node scripts/build -- --production\",\n    \"compile-tests\": \"tsc -p . --outDir out\",\n    \"watch-tests\": \"tsc -p . -w --outDir out\",\n    \"pretest\": \"npm run compile-tests && npm run compile && npm run lint\",\n    \"lint\": \"tsc -p . --noEmit\",\n    \"test\": \"node ./out/test/runTest.js\",\n    \"webview\": \"npx http-server ./webview -o -a localhost -p 8080\"\n  },\n  \"devDependencies\": {\n    \"@shikijs/types\": \"^3.13.0\",\n    \"@types/mocha\": \"^10.0.10\",\n    \"@types/node\": \"^16.11.68\",\n    \"@types/vscode\": \"^1.77.0\",\n    \"@types/vscode-webview\": \"^1.57.5\",\n    \"@vscode/codicons\": \"^0.0.41\",\n    \"@vscode/test-electron\": \"^2.5.2\",\n    \"esbuild\": \"^0.25.11\",\n    \"glob\": \"^13.0.6\",\n    \"mocha\": \"12.0.0-beta-10\",\n    \"npm-run-all\": \"^4.1.5\"\n  },\n  \"dependencies\": {\n    \"@pretty-ts-errors/formatter\": \"*\",\n    \"@pretty-ts-errors/utils\": \"*\",\n    \"@pretty-ts-errors/vscode-formatter\": \"*\",\n    \"shiki\": \"^3.13.0\",\n    \"vscode-languageclient\": \"^9.0.1\",\n    \"vscode-shiki-bridge\": \"^0.5.2\"\n  }\n}\n"
  },
  {
    "path": "apps/vscode-extension/scripts/build.js",
    "content": "const process = require(\"node:process\");\nconst console = require(\"node:console\");\nconst fs = require(\"node:fs\");\nconst path = require(\"node:path\");\nconst production = process.argv.includes(\"--production\");\nconst watch = process.argv.includes(\"--watch\");\n\n/**\n * @see https://code.visualstudio.com/api/working-with-extensions/bundling-extension#using-esbuild\n */\nasync function main() {\n  const ctx = await require(\"esbuild\").context({\n    entryPoints: {\n      extension: \"./src/extension.ts\",\n    },\n    bundle: true,\n    outdir: \"./dist\",\n    external: [\"vscode\"],\n    format: \"cjs\",\n    inject: [\"./scripts/process-shim.js\"],\n    tsconfig: \"./tsconfig.json\",\n    define: production ? { \"process.env.NODE_ENV\": '\"production\"' } : undefined,\n    minify: production,\n    sourcemap: !production,\n    plugins: [workspacePackagesPlugin, esbuildProblemMatcherPlugin],\n  });\n  if (watch) {\n    await ctx.watch();\n  } else {\n    fs.rmSync(\"./dist\", { recursive: true, force: true });\n    await ctx.rebuild();\n    await ctx.dispose();\n  }\n}\n\n/**\n * @type {import('esbuild').Plugin}\n */\nconst esbuildProblemMatcherPlugin = {\n  name: \"esbuild-problem-matcher\",\n  setup(build) {\n    build.onStart(() => {\n      console.log(\"[watch] build started\");\n    });\n    build.onEnd((result) => {\n      result.errors.forEach(({ text, location }) => {\n        console.error(`✘ [ERROR] ${text}`);\n        console.error(\n          `    ${location.file}:${location.line}:${location.column}:`\n        );\n      });\n      console.log(\"[watch] build finished\");\n    });\n  },\n};\n\n/**\n * Resolve internal workspace packages to their source files so we bundle them in watch/debug.\n * This makes changes in packages/* reflected immediately without separate watchers.\n * @type {import('esbuild').Plugin}\n */\nconst workspacePackagesPlugin = {\n  name: \"workspace-packages\",\n  setup(build) {\n    const pkgRoot = path.resolve(__dirname, \"../../../packages\");\n    /** @type {Record<string, string>} */\n    const alias = {\n      \"@pretty-ts-errors/utils\": path.join(pkgRoot, \"utils/src/index.ts\"),\n      \"@pretty-ts-errors/formatter\": path.join(\n        pkgRoot,\n        \"formatter/src/index.ts\"\n      ),\n      \"@pretty-ts-errors/vscode-formatter\": path.join(\n        pkgRoot,\n        \"vscode-formatter/src/index.ts\"\n      ),\n    };\n    build.onResolve(\n      { filter: /^@pretty-ts-errors\\/(utils|formatter|vscode-formatter)$/ },\n      (args) => {\n        const target = alias[args.path];\n        return target ? { path: target } : undefined;\n      }\n    );\n  },\n};\n\nmain().catch((e) => {\n  console.error(e);\n  process.exit(1);\n});\n"
  },
  {
    "path": "apps/vscode-extension/scripts/process-shim.js",
    "content": "// https://esbuild.github.io/api/#inject\n\nlet _cwd = \"/\";\n\nexport let process = {\n  cwd: () => _cwd,\n  chdir: (newCwd) => (_cwd = newCwd),\n  env: {},\n};\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/copyError.ts",
    "content": "import { commands, env, window, type ExtensionContext } from \"vscode\";\nimport { execute } from \"./execute\";\n\nconst COMMAND_ID = \"prettyTsErrors.copyError\";\n\nexport function registerCopyError(context: ExtensionContext) {\n  context.subscriptions.push(\n    commands.registerCommand(COMMAND_ID, async (errorMessage: unknown) =>\n      execute(COMMAND_ID, async () => {\n        if (typeof errorMessage !== \"string\") {\n          throw new Error(\"cannot write non-string value to clipboard\", {\n            cause: errorMessage,\n          });\n        }\n        await env.clipboard.writeText(errorMessage);\n        window.showInformationMessage(\"Copied error message to clipboard!\");\n      })\n    )\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/execute.ts",
    "content": "import { window } from \"vscode\";\nimport { logger } from \"../logger\";\n\n/**\n * A wrapper function to execute command tasks while providing user feedback and logging on errors.\n */\nexport async function execute(\n  commandName: string,\n  task: (...args: unknown[]) => unknown | Promise<unknown>\n) {\n  try {\n    return await task();\n  } catch (error) {\n    if (error instanceof Error) {\n      logger.error(error);\n    } else if (typeof error === \"string\") {\n      logger.error(error);\n    } else {\n      logger.error(\"caught non-string or error value: \", error);\n    }\n    window.showErrorMessage(`Failed to execute command: '${commandName}'`);\n    throw error;\n  }\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/pinError.ts",
    "content": "import { commands, type ExtensionContext } from \"vscode\";\nimport { execute } from \"./execute\";\nimport { tryEnsureRange } from \"./validate\";\nimport { getViewProvider } from \"../provider/webviewViewProvider\";\n\nconst COMMAND_ID = \"prettyTsErrors.pinError\";\n\nexport function registerPinError(context: ExtensionContext) {\n  context.subscriptions.push(\n    commands.registerCommand(\n      COMMAND_ID,\n      async (maybeRangeLike: unknown, maybeMessage?: unknown) =>\n        execute(COMMAND_ID, async () => {\n          const { isValidRange, range } = tryEnsureRange(maybeRangeLike);\n          if (!isValidRange) {\n            throw new Error(\"cannot pin error with an invalid range\", {\n              cause: maybeRangeLike,\n            });\n          }\n          const message =\n            typeof maybeMessage === \"string\" ? maybeMessage : undefined;\n\n          const viewProvider = getViewProvider();\n          await viewProvider?.pinDiagnostic(range, message);\n\n          try {\n            await commands.executeCommand(\"prettyTsErrors.sidePanel.focus\");\n          } catch {}\n        })\n    )\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/revealSelection.ts",
    "content": "import { type ExtensionContext, commands } from \"vscode\";\nimport { tryEnsureRange, tryEnsureUri } from \"./validate\";\nimport { execute } from \"./execute\";\n\nconst COMMAND_ID = \"prettyTsErrors.revealSelection\";\n\nexport function registerRevealSelection(context: ExtensionContext) {\n  context.subscriptions.push(\n    commands.registerCommand(\n      COMMAND_ID,\n      async (maybeUriLike: unknown, maybeRangeLike: unknown) =>\n        execute(COMMAND_ID, async () => {\n          const { isValidUri, uri } = tryEnsureUri(maybeUriLike);\n          const { isValidRange, range } = tryEnsureRange(maybeRangeLike);\n          if (!isValidUri || !isValidRange) {\n            throw new Error(\n              \"cannot reveal selection with invalid range or uri\",\n              {\n                cause: {\n                  range: maybeRangeLike,\n                  uri: maybeUriLike,\n                },\n              }\n            );\n          }\n          return commands.executeCommand(\"vscode.open\", uri, {\n            selection: range,\n          });\n        })\n    )\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/showErrorInSidebar.ts",
    "content": "import { commands, type ExtensionContext } from \"vscode\";\nimport { execute } from \"./execute\";\nimport { tryEnsureRange } from \"./validate\";\nimport { getViewProvider } from \"../provider/webviewViewProvider\";\n\nconst COMMAND_ID = \"prettyTsErrors.showErrorInSidebar\";\n\nexport function registerShowErrorInSidebar(context: ExtensionContext) {\n  context.subscriptions.push(\n    commands.registerCommand(\n      COMMAND_ID,\n      async (maybeRangeLike?: unknown, maybeMessage?: unknown) =>\n        execute(COMMAND_ID, async () => {\n          if (maybeRangeLike !== undefined) {\n            const { isValidRange, range } = tryEnsureRange(maybeRangeLike);\n            const message =\n              typeof maybeMessage === \"string\" ? maybeMessage : undefined;\n            const viewProvider = getViewProvider();\n            if (isValidRange && viewProvider) {\n              await viewProvider.lockToDiagnostic(range, message);\n            }\n          }\n\n          try {\n            await commands.executeCommand(\n              \"workbench.view.extension.prettyTsErrors\"\n            );\n          } catch {}\n        })\n    )\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/unpinError.ts",
    "content": "import { commands, type ExtensionContext } from \"vscode\";\nimport { execute } from \"./execute\";\nimport { getViewProvider } from \"../provider/webviewViewProvider\";\n\nconst COMMAND_ID = \"prettyTsErrors.unpinError\";\n\nexport function registerUnpinError(context: ExtensionContext) {\n  context.subscriptions.push(\n    commands.registerCommand(COMMAND_ID, async () =>\n      execute(COMMAND_ID, async () => {\n        const viewProvider = getViewProvider();\n        viewProvider?.unpinDiagnostic();\n      })\n    )\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/commands/validate.ts",
    "content": "import { Range, Uri } from \"vscode\";\n\nexport function tryEnsureUri(\n  maybeUriLike: unknown\n): { isValidUri: true; uri: Uri } | { isValidUri: false; uri?: undefined } {\n  if (maybeUriLike instanceof Uri) {\n    return { isValidUri: true, uri: maybeUriLike };\n  }\n  if (typeof maybeUriLike === \"string\") {\n    try {\n      return { isValidUri: true, uri: Uri.parse(maybeUriLike, true) };\n    } catch (error) {\n      return { isValidUri: false };\n    }\n  }\n  if (isUriLike(maybeUriLike)) {\n    return { isValidUri: true, uri: Uri.from(maybeUriLike) };\n  }\n  return { isValidUri: false };\n}\n\ntype UriLike = Parameters<typeof Uri.from>[0];\n\nfunction isUriLike(value: unknown): value is UriLike {\n  return (\n    typeof value === \"object\" &&\n    value != null &&\n    \"scheme\" in value &&\n    typeof value.scheme === \"string\"\n  );\n}\n\nexport function tryEnsureRange(\n  maybeRangeLike: unknown\n):\n  | { isValidRange: true; range: Range }\n  | { isValidRange: false; range?: undefined } {\n  if (maybeRangeLike instanceof Range) {\n    return { isValidRange: true, range: maybeRangeLike };\n  }\n  if (isRangeLike(maybeRangeLike)) {\n    return {\n      isValidRange: true,\n      range: new Range(\n        maybeRangeLike.start.line,\n        maybeRangeLike.start.character,\n        maybeRangeLike.end.line,\n        maybeRangeLike.end.character\n      ),\n    };\n  }\n  return { isValidRange: false };\n}\n\ntype RangeLike = { start: PositionLike; end: PositionLike };\n\nfunction isRangeLike(value: unknown): value is RangeLike {\n  return (\n    typeof value === \"object\" &&\n    value != null &&\n    \"start\" in value &&\n    isPositionLike(value.start) &&\n    \"end\" in value &&\n    isPositionLike(value.end)\n  );\n}\n\ntype PositionLike = { line: number; character: number };\n\nfunction isPositionLike(value: unknown): value is PositionLike {\n  return (\n    typeof value === \"object\" &&\n    value !== null &&\n    \"line\" in value &&\n    typeof value.line === \"number\" &&\n    \"character\" in value &&\n    typeof value.character === \"number\"\n  );\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/diagnostics.ts",
    "content": "import { has } from \"@pretty-ts-errors/utils\";\nimport { prettifyDiagnosticForHover } from \"@pretty-ts-errors/vscode-formatter\";\nimport {\n  ExtensionContext,\n  languages,\n  MarkdownString,\n  window,\n  Uri,\n  type Diagnostic,\n} from \"vscode\";\nimport {\n  createConverter,\n  type Converter,\n} from \"vscode-languageclient/lib/common/codeConverter\";\nimport { hoverProvider } from \"./provider/hoverProvider\";\nimport {\n  formattedDiagnosticsStore,\n  type FormattedDiagnostic,\n} from \"./formattedDiagnosticsStore\";\nimport { logger } from \"./logger\";\n\n/**\n * The list of diagnostic sources that pretty-ts-errors supports\n */\nconst supportedDiagnosticSources = [\n  \"ts\",\n  \"ts-plugin\",\n  \"deno-ts\",\n  \"js\",\n  \"glint\",\n];\n\nexport function registerOnDidChangeDiagnostics(context: ExtensionContext) {\n  const converter = createConverter();\n  context.subscriptions.push(\n    languages.onDidChangeDiagnostics(async (e) => {\n      await logger.measure(\"onDidChangeDiagnostics\", async () => {\n        for (const uri of e.uris) {\n          await logger.measure(\n            `diagnostics for: ${uri.toString(true)}`,\n            async () => {\n              const diagnostics = languages.getDiagnostics(uri);\n              const supportedDiagnostics = diagnostics.filter(\n                (diagnostic) =>\n                  diagnostic.source &&\n                  has(supportedDiagnosticSources, diagnostic.source)\n              );\n\n              const items: FormattedDiagnostic[] = await Promise.all(\n                supportedDiagnostics.map((diagnostic) =>\n                  getFormattedDiagnostic(diagnostic, converter)\n                )\n              );\n\n              // TODO: we should check if never deleting the entries is a performance issue\n              //       probably not, since solving all diagnostics for a file should set its value to an empty collection, but we should check anyway\n              //       see: https://github.com/yoavbls/pretty-ts-errors/issues/139\n              formattedDiagnosticsStore.set(uri.fsPath, items);\n\n              if (items.length > 0) {\n                ensureHoverProviderIsRegistered(uri, context);\n              }\n            }\n          );\n        }\n      });\n    })\n  );\n}\n\n/**\n * To prevent infinite memory consumption use a max size for the cache\n *\n * TODO: consider making this configurable to the end user with a sensible `min` and `max`\n */\nconst CACHE_SIZE_MAX = 100;\n\n/**\n * A local cache that maps TS diagnostics as `string` to their formatted `MarkdownString` counter part.\n * @see https://github.com/yoavbls/pretty-ts-errors/pull/62\n *\n * One reason this cache is critical is because the TypeScript Language Features extension is very noisy and will constantly push all diagnostics for a file,\n * even if there were no actual changes.\n * @see https://github.com/yoavbls/pretty-ts-errors/issues/139#issuecomment-3401279357\n *\n * TODO: create a proper LRU cache, to prevent exceeding the cache size being a bottleneck\n * @see https://github.com/yoavbls/pretty-ts-errors/issues/104\n */\nconst cache = new Map<string, MarkdownString>();\n\nasync function getFormattedDiagnostic(\n  diagnostic: Diagnostic,\n  converter: Converter\n): Promise<FormattedDiagnostic> {\n  // formatDiagnosticForHover converts message based on LSP Diagnostic type, not VSCode Diagnostic type, so it can be used in other IDEs.\n  // Here we convert VSCode Diagnostic to LSP Diagnostic to make formatDiagnosticForHover recognize it.\n  const lspDiagnostic = converter.asDiagnostic(diagnostic);\n\n  let formattedMessage = cache.get(diagnostic.message);\n  if (!formattedMessage) {\n    const formattedDiagnostic = await prettifyDiagnosticForHover(lspDiagnostic);\n    const markdownString = new MarkdownString(formattedDiagnostic);\n\n    // TODO: consider using the `{ enabledCommands: string[] }` variant, to only allow whitelisted commands\n    markdownString.isTrusted = true;\n    markdownString.supportHtml = true;\n\n    formattedMessage = markdownString;\n    if (cache.size > CACHE_SIZE_MAX) {\n      const firstCacheKey = cache.keys().next().value!;\n      cache.delete(firstCacheKey);\n    }\n    cache.set(diagnostic.message, formattedMessage);\n  }\n\n  return {\n    range: diagnostic.range,\n    contents: [formattedMessage],\n    lspDiagnostic,\n  };\n}\n\n/**\n * A set to prevent registering duplicate hover providers.\n */\nconst registeredLanguages = new Set<string>();\n\n/**\n * Ensure a hover provider is registered for any visible editors where pretty-ts-errors has a formatted diagnostic\n */\nfunction ensureHoverProviderIsRegistered(uri: Uri, context: ExtensionContext) {\n  const editor = window.visibleTextEditors.find(\n    (editor) => editor.document.uri.toString() === uri.toString()\n  );\n  const languageId = editor?.document.languageId;\n  if (languageId && !registeredLanguages.has(languageId)) {\n    logger.debug(`registering hover provider for language id: ${languageId}`);\n    registeredLanguages.add(languageId);\n    context.subscriptions.push(\n      languages.registerHoverProvider(\n        {\n          language: languageId,\n        },\n        hoverProvider\n      )\n    );\n  }\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/extension.ts",
    "content": "import { ExtensionContext, commands } from \"vscode\";\nimport { registerOnDidChangeDiagnostics } from \"./diagnostics\";\nimport { logger } from \"./logger\";\nimport { registerCopyError } from \"./commands/copyError\";\nimport { registerRevealSelection } from \"./commands/revealSelection\";\nimport { registerShowErrorInSidebar } from \"./commands/showErrorInSidebar\";\nimport { registerPinError } from \"./commands/pinError\";\nimport { registerUnpinError } from \"./commands/unpinError\";\nimport { registerSelectedTextHoverProvider } from \"./provider/selectedTextHoverProvider\";\nimport { registerWebviewViewProvider } from \"./provider/webviewViewProvider\";\nimport { SUPPORTED_LANGUAGE_IDS } from \"./supportedLanguageIds\";\n\nexport function activate(context: ExtensionContext) {\n  logger.info(\"activating\");\n\n  void commands.executeCommand(\n    \"setContext\",\n    \"prettyTsErrors.supportedLanguageIds\",\n    SUPPORTED_LANGUAGE_IDS\n  );\n\n  // logging and debug features\n  logger.register(context);\n  registerSelectedTextHoverProvider(context);\n\n  // prettify diagnostics feature\n  registerOnDidChangeDiagnostics(context);\n\n  // UI elements that show the prettified diagnostics\n  registerWebviewViewProvider(context);\n\n  // register commands\n  registerCopyError(context);\n  registerShowErrorInSidebar(context);\n  registerPinError(context);\n  registerUnpinError(context);\n  registerRevealSelection(context);\n}\n\nexport function deactivate() {\n  logger.info(\"deactivating\");\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/formattedDiagnosticsStore.ts",
    "content": "import { MarkdownString, Range, Uri } from \"vscode\";\nimport type { Diagnostic } from \"vscode-languageserver-types\";\n\ntype StoreKey = Uri[\"fsPath\"];\n\nexport interface FormattedDiagnostic {\n  range: Range;\n  contents: MarkdownString[];\n  /** Original LSP diagnostic for on-demand sidebar formatting */\n  lspDiagnostic: Diagnostic;\n}\n\n/**\n * A store for formatted diagnostics, where the key is the file path to a file, and the value a collection of formatted diagnostics for that file.\n *\n * The `onDidChangeDiagnostics` event handler will fill the store with formatted diagnostics, while other components will query the store to display these diagnostics.\n */\nexport const formattedDiagnosticsStore = new Map<\n  StoreKey,\n  FormattedDiagnostic[]\n>();\n"
  },
  {
    "path": "apps/vscode-extension/src/globals.d.ts",
    "content": "// \"@types/node\": \"^16.11.68\" misses a bunch of global declarations, this file fixes the one's we use.\n\ndeclare global {\n  declare const TextDecoder: new (\n    encoding?: string,\n    options?: {\n      fatal?: boolean | undefined;\n      ignoreBOM?: boolean | undefined;\n    }\n  ) => import(\"util\").TextDecoder;\n}\n\nexport {};\n"
  },
  {
    "path": "apps/vscode-extension/src/logger.ts",
    "content": "import {\n  ExtensionMode,\n  LogOutputChannel,\n  window,\n  type ExtensionContext,\n  LogLevel as VSLogLevel,\n} from \"vscode\";\n\nlet instance: null | LogOutputChannel = null;\n\nfunction getLogger(): LogOutputChannel {\n  if (instance !== null) {\n    return instance;\n  }\n  instance = window.createOutputChannel(\"Pretty TypeScript Errors\", {\n    log: true,\n  });\n  return instance;\n}\n\nfunction info(...args: Parameters<LogOutputChannel[\"info\"]>) {\n  getLogger().info(...args);\n}\n\nfunction trace(...args: Parameters<LogOutputChannel[\"trace\"]>) {\n  getLogger().trace(...args);\n}\n\nfunction debug(...args: Parameters<LogOutputChannel[\"debug\"]>) {\n  getLogger().debug(...args);\n}\nfunction warn(...args: Parameters<LogOutputChannel[\"warn\"]>) {\n  getLogger().warn(...args);\n}\n\nfunction error(...args: Parameters<LogOutputChannel[\"error\"]>) {\n  getLogger().error(...args);\n}\n\ntype LogLevel = \"info\" | \"trace\" | \"debug\" | \"warn\" | \"error\";\ntype LogLevelThresholds = Record<LogLevel, number>;\ntype SortedLogLevelThresholds = [LogLevel, number][];\n\nconst defaultThresholds: LogLevelThresholds = {\n  error: 5000,\n  warn: 1000,\n  info: 100,\n  debug: 50,\n  trace: 0,\n};\n\n/**\n * Both in the browser and Node >= 16 (vscode 1.77 has node >= 16) have `performance` available as a global\n * But `@types/node` is missing its global declaration, this fixes the type error we get from using it\n */\ndeclare const performance: import(\"perf_hooks\").Performance;\n\n/**\n * Measures the time it took to run `task` and reports it to the `logger` based on `logLevelThresholds`.\n *\n * @see {@link defaultThresholds} for the default thresholds\n */\nfunction measure<T = unknown>(\n  name: string,\n  task: () => Promise<T>,\n  logLevelThresholds?: Partial<LogLevelThresholds>\n): Promise<T>;\nfunction measure<T = unknown>(\n  name: string,\n  task: () => T,\n  logLevelThresholds?: Partial<LogLevelThresholds>\n): T;\nfunction measure<T = unknown>(\n  name: string,\n  task: () => T | Promise<T>,\n  logLevelThresholds: Partial<LogLevelThresholds> = {}\n): T | Promise<T> {\n  const start = performance.now();\n  const thresholds = normalizeThresholds(logLevelThresholds);\n\n  try {\n    const result = task();\n    if (isPromiseLike(result)) {\n      return result.then(\n        (value) => {\n          logMeasuredDuration(name, start, thresholds);\n          return value;\n        },\n        (error) => {\n          logMeasuredDuration(name, start, thresholds);\n          throw error;\n        }\n      );\n    }\n    logMeasuredDuration(name, start, thresholds);\n    return result;\n  } catch (error) {\n    logMeasuredDuration(name, start, thresholds);\n    throw error;\n  }\n}\n\nfunction logMeasuredDuration(\n  name: string,\n  start: number,\n  thresholds: SortedLogLevelThresholds\n) {\n  const duration = performance.now() - start;\n  const level = findLogLevel(thresholds, duration);\n  getLogger()[level](`task ${name} took ${duration.toFixed(3)}ms`);\n}\n\nfunction normalizeThresholds(\n  logLevelThresholds: Partial<LogLevelThresholds>\n): SortedLogLevelThresholds {\n  logLevelThresholds = Object.assign({}, defaultThresholds, logLevelThresholds);\n  // sort thresholds from high to low\n  // { info: 100, warn: 1000, trace: 0 } => [[warn, 1000], [info, 100], [trace, 0]]\n  return Object.entries(logLevelThresholds).sort(\n    ([_a, a], [_b, b]) => b - a\n  ) as SortedLogLevelThresholds;\n}\n\nfunction findLogLevel(\n  thresholds: SortedLogLevelThresholds,\n  duration: number,\n  defaultLogLevel: LogLevel = \"debug\"\n): LogLevel {\n  return (\n    thresholds.find(([_, threshold]) => duration > threshold)?.[0] ??\n    defaultLogLevel\n  );\n}\n\nfunction dispose() {\n  if (instance !== null) {\n    instance.dispose();\n    instance = null;\n  }\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n  return (\n    value != null &&\n    typeof value === \"object\" &&\n    \"then\" in value &&\n    typeof value[\"then\"] === \"function\"\n  );\n}\n\nfunction register(context: ExtensionContext) {\n  if (context.extensionMode === ExtensionMode.Development) {\n    const instance = getLogger();\n    instance.show();\n    if (instance.logLevel !== VSLogLevel.Trace) {\n      instance.appendLine(\n        `To see more verbose logging, set this output's log level to \"Trace\" (gear icon next to the dropdown).`\n      );\n    }\n  }\n  context.subscriptions.push({ dispose });\n}\n\nexport const logger = {\n  trace,\n  debug,\n  info,\n  warn,\n  error,\n  measure,\n  register,\n  dispose,\n};\n"
  },
  {
    "path": "apps/vscode-extension/src/provider/hoverProvider.ts",
    "content": "import { HoverProvider } from \"vscode\";\nimport { formattedDiagnosticsStore } from \"../formattedDiagnosticsStore\";\n\nexport const hoverProvider: HoverProvider = {\n  provideHover(document, position, _token) {\n    const items = formattedDiagnosticsStore.get(document.uri.fsPath);\n\n    if (!items) {\n      return null;\n    }\n\n    const itemInRange = items.filter((item) => item.range.contains(position));\n\n    if (itemInRange.length === 0) {\n      return null;\n    }\n\n    const first = itemInRange[0];\n    if (!first) {\n      return null;\n    }\n    return {\n      range: first.range,\n      contents: itemInRange.flatMap((item) => item.contents),\n    };\n  },\n};\n"
  },
  {
    "path": "apps/vscode-extension/src/provider/markdownWebviewProvider.ts",
    "content": "import * as vscode from \"vscode\";\n\n/**\n * @see https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample\n */\nexport class MarkdownWebviewProvider {\n  private webviewRootUri: vscode.Uri;\n  private webviewHtmlTemplate: Promise<string>;\n\n  constructor(private readonly context: vscode.ExtensionContext) {\n    this.webviewRootUri = vscode.Uri.joinPath(\n      this.context.extensionUri,\n      \"webview\"\n    );\n    this.webviewHtmlTemplate = this.loadWebviewHtmlTemplate();\n  }\n\n  private async loadWebviewHtmlTemplate(): Promise<string> {\n    const htmlTemplateUri = vscode.Uri.joinPath(\n      this.webviewRootUri,\n      \"index.html\"\n    );\n    const htmlTemplateBytes =\n      await vscode.workspace.fs.readFile(htmlTemplateUri);\n    const htmlTemplate = new TextDecoder(\"utf-8\").decode(htmlTemplateBytes);\n    return htmlTemplate;\n  }\n\n  getWebviewOptions(): vscode.WebviewOptions {\n    return {\n      enableCommandUris: [\n        \"prettyTsErrors.revealSelection\",\n        \"prettyTsErrors.copyError\",\n        \"prettyTsErrors.pinError\",\n        \"prettyTsErrors.unpinError\",\n      ],\n      enableScripts: true,\n      enableForms: false,\n      localResourceRoots: [this.webviewRootUri],\n    };\n  }\n\n  createOnDidReceiveMessage() {\n    return (message: { command: string; [key: string]: unknown }) => {\n      if (message && message.command) {\n        switch (message.command) {\n          case \"notify\": {\n            if (typeof message[\"text\"] === \"string\") {\n              vscode.window.showInformationMessage(message[\"text\"]);\n            }\n            break;\n          }\n        }\n      }\n    };\n  }\n\n  async getWebviewContent(\n    webview: vscode.Webview,\n    content: string,\n    classList: string[] = []\n  ): Promise<string> {\n    const template = await this.webviewHtmlTemplate;\n    const html = this.patchCspSafeAttrs(template, webview);\n    return html.replace(\n      '<div id=\"content\"></div>',\n      `<div id=\"content\" class=\"${classList.join(\" \")}\">${content}</div>`\n    );\n  }\n\n  private patchCspSafeAttrs(html: string, webview: vscode.Webview) {\n    // replace stylesheet href's to webview uri's\n    html = html.replaceAll(\n      /<link\\s+rel=\"stylesheet\"\\s+href=\"(\\.\\/.+)\"\\s+data-href-as-webview-uri\\s*\\/?>/gm,\n      (match, filePath) => {\n        const path = vscode.Uri.joinPath(this.webviewRootUri, filePath);\n        const uri = webview.asWebviewUri(path);\n        return match.replace(filePath, uri.toString());\n      }\n    );\n\n    // replace script src's to webiew uri's\n    html = html.replaceAll(\n      /<script\\s+src=\"(\\.\\/.+)\"\\s+data-src-as-webview-uri\\s*>/gm,\n      (match, filePath) => {\n        const path = vscode.Uri.joinPath(this.webviewRootUri, filePath);\n        const uri = webview.asWebviewUri(path);\n        return match.replace(filePath, uri.toString());\n      }\n    );\n\n    // replace the local development csp header with `webview.cspSource`\n    // @see https://code.visualstudio.com/api/extension-guides/webview#content-security-policy\n    html = html.replace(\n      /<meta\\s+http-equiv=\"Content-Security-Policy\"\\s+data-csp-replace-content\\s+content=\"(.+)\"\\s*\\/>/m,\n      (match, content) => {\n        return match.replace(\n          content,\n          content\n            .replaceAll(\n              \"style-src http://localhost:8080\",\n              // TODO: remove `unsafe-inline` if vscode ever fixes their styles and api injection\n              `style-src ${webview.cspSource} 'unsafe-inline'`\n            )\n            .replaceAll(\n              \"script-src http://localhost:8080\",\n              // TODO: remove `unsafe-inline` if vscode ever fixes their styles and api injection\n              `script-src ${webview.cspSource} 'unsafe-inline'`\n            )\n            .replaceAll(\n              \"font-src http://localhost:8080\",\n              `font-src ${webview.cspSource}`\n            )\n        );\n      }\n    );\n    return html;\n  }\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/provider/selectedTextHoverProvider.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\nimport { prettifyDiagnosticForHover } from \"@pretty-ts-errors/vscode-formatter\";\nimport {\n  ExtensionContext,\n  ExtensionMode,\n  MarkdownString,\n  languages,\n  window,\n} from \"vscode\";\nimport { createConverter } from \"vscode-languageclient/lib/common/codeConverter\";\nimport { formattedDiagnosticsStore } from \"../formattedDiagnosticsStore\";\n\n/**\n * Register an hover provider in debug only.\n * It format selected text and help test things visually easier.\n */\nexport function registerSelectedTextHoverProvider(context: ExtensionContext) {\n  if (context.extensionMode !== ExtensionMode.Development) {\n    return;\n  }\n\n  const converter = createConverter();\n  context.subscriptions.push(\n    languages.registerHoverProvider(\n      {\n        language: \"typescript\",\n        pattern: \"**/test/**/*.ts\",\n      },\n      {\n        async provideHover(document, position) {\n          const editor = window.activeTextEditor;\n\n          if (!editor) {\n            return;\n          }\n\n          const range = document.getWordRangeAtPosition(position);\n          const message = editor ? document.getText(editor.selection) : \"\";\n\n          if (!range || !message) {\n            return null;\n          }\n\n          const lspDiagnostic = converter.asDiagnostic({\n            message,\n            range,\n            severity: 0,\n            source: \"ts\",\n            code: 1337,\n          });\n\n          const markdown = new MarkdownString(\n            debugHoverHeader + (await prettifyDiagnosticForHover(lspDiagnostic))\n          );\n\n          markdown.isTrusted = true;\n          markdown.supportHtml = true;\n\n          formattedDiagnosticsStore.set(document.uri.fsPath, [\n            {\n              range,\n              contents: [markdown],\n              lspDiagnostic,\n            },\n          ]);\n\n          return {\n            contents: [markdown],\n          };\n        },\n      }\n    )\n  );\n}\n\nconst debugHoverHeader = d /*html*/ `\n  <span style=\"color:#f96363;\">\n    <span class=\"codicon codicon-debug\"></span>\n    Formatted selected text (debug only)\n  </span>\n  <br>\n  <hr>\n  <p></p>\n`;\n"
  },
  {
    "path": "apps/vscode-extension/src/provider/webviewViewProvider.ts",
    "content": "import type { ExtensionContext } from \"vscode\";\nimport * as vscode from \"vscode\";\nimport { getUserLangs, getUserTheme } from \"vscode-shiki-bridge\";\nimport { createHighlighterCore } from \"shiki/core\";\nimport { createOnigurumaEngine } from \"shiki/engine/oniguruma\";\nimport { MarkdownWebviewProvider } from \"./markdownWebviewProvider\";\nimport {\n  formattedDiagnosticsStore,\n  type FormattedDiagnostic,\n} from \"../formattedDiagnosticsStore\";\nimport { has } from \"@pretty-ts-errors/utils\";\nimport {\n  prettifyDiagnosticForSidebar,\n  initHighlighter,\n} from \"@pretty-ts-errors/vscode-formatter\";\nimport { SUPPORTED_LANGUAGE_IDS } from \"../supportedLanguageIds\";\n\nconst NO_DIAGNOSTICS_MESSAGE =\n  \"Select code with an error to show the prettified diagnostic in this view.\";\n\ntype ViewMode = \"cursor\" | \"locked\";\n\ninterface DiagnosticItem {\n  html: string;\n  range: vscode.Range;\n}\n\ninterface PinnedError {\n  html: string;\n}\n\nlet viewProviderInstance: MarkdownWebviewViewProvider | null = null;\n\nexport function getViewProvider() {\n  return viewProviderInstance;\n}\n\nfunction updateHasErrorsContext() {\n  const editor = vscode.window.activeTextEditor;\n  if (editor && has(SUPPORTED_LANGUAGE_IDS, editor.document.languageId)) {\n    const diagnostics = vscode.languages.getDiagnostics(editor.document.uri);\n    const hasErrors = diagnostics.length > 0;\n    vscode.commands.executeCommand(\n      \"setContext\",\n      \"prettyTsErrors.hasErrors\",\n      hasErrors\n    );\n  } else {\n    vscode.commands.executeCommand(\n      \"setContext\",\n      \"prettyTsErrors.hasErrors\",\n      false\n    );\n  }\n}\n\nexport function registerWebviewViewProvider(context: ExtensionContext) {\n  viewProviderInstance = new MarkdownWebviewViewProvider(\n    new MarkdownWebviewProvider(context)\n  );\n  context.subscriptions.push(\n    vscode.window.registerWebviewViewProvider(\n      \"prettyTsErrors.sidePanel\",\n      viewProviderInstance\n    ),\n    vscode.languages.onDidChangeDiagnostics(() => updateHasErrorsContext()),\n    vscode.window.onDidChangeActiveTextEditor(() => updateHasErrorsContext())\n  );\n  updateHasErrorsContext();\n}\n\nasync function diagnosticToItem(\n  formattedDiagnostic: FormattedDiagnostic\n): Promise<DiagnosticItem> {\n  return {\n    html: await prettifyDiagnosticForSidebar(formattedDiagnostic.lspDiagnostic),\n    range: formattedDiagnostic.range,\n  };\n}\n\n// TODO: adding a `MarkdownWebviewView` class would make this provider a lot simpler\nclass MarkdownWebviewViewProvider implements vscode.WebviewViewProvider {\n  private disposables = new Map<vscode.WebviewView, vscode.Disposable[]>();\n  private webview: vscode.Webview | null = null;\n  private mode: ViewMode = \"cursor\";\n  private lockedContent: DiagnosticItem | null = null;\n  private pinnedError: PinnedError | null = null;\n  private lastContent: string | null = null;\n  private skipNextSelectionChange = false;\n  private skipNextEditorChange = false;\n  private initialized = false;\n\n  constructor(private readonly provider: MarkdownWebviewProvider) {}\n\n  private async ensureInitialized() {\n    if (!this.initialized) {\n      const [theme, themes] = await getUserTheme();\n\n      const langs = await getUserLangs([\"type\", \"ts\"]);\n      const highlighter = await createHighlighterCore({\n        themes,\n        langs,\n        engine: createOnigurumaEngine(import(\"shiki/wasm\")),\n      });\n      initHighlighter({\n        codeToHtml: (code: string, options: { lang: string }) =>\n          highlighter.codeToHtml(code, { ...options, theme }),\n      });\n      this.initialized = true;\n    }\n  }\n\n  async lockToDiagnostic(range: vscode.Range, message?: string) {\n    const activeEditor = vscode.window.activeTextEditor;\n    if (activeEditor) {\n      const diagnostics =\n        formattedDiagnosticsStore.get(activeEditor.document.uri.fsPath) ?? [];\n      const diagnostic = diagnostics.find(\n        (diagnostic) =>\n          diagnostic.range.isEqual(range) &&\n          (!message || diagnostic.lspDiagnostic.message === message)\n      );\n      if (diagnostic) {\n        await this.ensureInitialized();\n        this.mode = \"locked\";\n        this.lockedContent = await diagnosticToItem(diagnostic);\n        this.skipNextSelectionChange = true;\n        this.skipNextEditorChange = true;\n        this.lastContent = null;\n        if (this.webview) {\n          this.refresh(this.webview);\n        }\n      }\n    }\n  }\n\n  async pinDiagnostic(range: vscode.Range, message?: string) {\n    const activeEditor = vscode.window.activeTextEditor;\n    if (!activeEditor) return;\n\n    const diagnostics =\n      formattedDiagnosticsStore.get(activeEditor.document.uri.fsPath) ?? [];\n    const diagnostic = diagnostics.find(\n      (diagnostic) =>\n        diagnostic.range.isEqual(range) &&\n        (!message || diagnostic.lspDiagnostic.message === message)\n    );\n    if (!diagnostic) return;\n\n    await this.ensureInitialized();\n    const item = await diagnosticToItem(diagnostic);\n\n    // Toggle: if already pinned, unpin instead\n    if (this.pinnedError && this.pinnedError.html === item.html) {\n      this.pinnedError = null;\n    } else {\n      this.pinnedError = { html: item.html };\n    }\n\n    if (this.webview) {\n      this.refresh(this.webview);\n    }\n  }\n\n  unpinDiagnostic() {\n    this.pinnedError = null;\n    if (this.webview) {\n      this.refresh(this.webview);\n    }\n  }\n\n  async resolveWebviewView(\n    webviewView: vscode.WebviewView,\n    _context: vscode.WebviewViewResolveContext\n  ): Promise<void> {\n    this.webview = webviewView.webview;\n\n    const initialContent = await this.getActiveContentHtml();\n    webviewView.webview.html = await this.provider.getWebviewContent(\n      webviewView.webview,\n      initialContent,\n      [\"webview-panel\"]\n    );\n\n    const disposables = this.ensureDisposables(webviewView);\n\n    webviewView.webview.options = this.provider.getWebviewOptions();\n    disposables.push(\n      webviewView.webview.onDidReceiveMessage(\n        this.provider.createOnDidReceiveMessage()\n      ),\n      vscode.languages.onDidChangeDiagnostics(() =>\n        // TODO: since `onDidChangeDiagnostics` fires often, we should try and avoid calling refresh based on the event uris\n        this.refresh(webviewView.webview)\n      ),\n      vscode.window.onDidChangeActiveTextEditor((editor) => {\n        if (this.skipNextEditorChange) {\n          this.skipNextEditorChange = false;\n          return;\n        }\n        if (editor) {\n          if (this.mode === \"locked\") {\n            this.mode = \"cursor\";\n          }\n          this.refresh(webviewView.webview);\n        }\n      }),\n      vscode.window.onDidChangeTextEditorSelection((event) => {\n        const document = event.textEditor.document;\n        // this event fires often, including selecting text in output windows and terminal windows\n        // avoid doing unnessesary work, because it will cause noticable delays in the UI\n        if (!has(SUPPORTED_LANGUAGE_IDS, document.languageId)) {\n          return;\n        }\n        if (this.skipNextSelectionChange) {\n          this.skipNextSelectionChange = false;\n          return;\n        }\n        if (this.mode === \"locked\") {\n          this.mode = \"cursor\";\n        }\n        if (this.mode === \"cursor\") {\n          this.refresh(webviewView.webview);\n        }\n      }),\n      webviewView.onDidChangeVisibility(() => {\n        if (webviewView.visible) {\n          this.lastContent = null;\n          this.refresh(webviewView.webview);\n        }\n      })\n    );\n\n    webviewView.onDidDispose(() => {\n      const disposables = this.disposables.get(webviewView);\n      disposables?.forEach((disposable) => disposable.dispose());\n      this.disposables.delete(webviewView);\n      this.webview = null;\n    });\n  }\n\n  private ensureDisposables(webviewView: vscode.WebviewView) {\n    let disposables = this.disposables.get(webviewView);\n    if (!disposables) {\n      disposables = [];\n      this.disposables.set(webviewView, disposables);\n    }\n    return disposables;\n  }\n\n  private async getActiveContentHtml(): Promise<string> {\n    const items = await this.getActiveDiagnosticItems();\n    if (items.length === 0) return NO_DIAGNOSTICS_MESSAGE;\n    return items.map((item) => item.html).join(\"<hr>\");\n  }\n\n  private async getActiveDiagnosticItems(): Promise<DiagnosticItem[]> {\n    await this.ensureInitialized();\n    switch (this.mode) {\n      case \"cursor\":\n        return this.getCursorDiagnosticItems();\n      case \"locked\":\n        return this.lockedContent ? [this.lockedContent] : [];\n    }\n  }\n\n  private async getCursorDiagnosticItems(): Promise<DiagnosticItem[]> {\n    const activeEditor = vscode.window.activeTextEditor;\n    const selection = activeEditor?.selection;\n    if (!activeEditor || !selection) return [];\n\n    const diagnostics =\n      formattedDiagnosticsStore.get(activeEditor.document.uri.fsPath) ?? [];\n    const selectedDiagnostics = diagnostics.filter(\n      (diagnostic) => diagnostic.range.intersection(selection) !== undefined\n    );\n    return Promise.all(selectedDiagnostics.map((d) => diagnosticToItem(d)));\n  }\n\n  async refresh(webview: vscode.Webview) {\n    const sections: string[] = [];\n\n    // Render pinned error section\n    if (this.pinnedError) {\n      sections.push(\n        `<div class=\"pinned-section\">` +\n          `<div class=\"pinned-header\">` +\n          `<span class=\"pinned-label\">` +\n          `<span class=\"codicon codicon-pinned\"></span>` +\n          ` Pinned error` +\n          `</span>` +\n          `<a class=\"unpin-button codicon codicon-close\" title=\"Unpin error\" href=\"command:prettyTsErrors.unpinError\"></a>` +\n          `</div>` +\n          this.pinnedError.html +\n          `</div>`\n      );\n      sections.push(`<hr>`);\n    }\n\n    // Render active diagnostic items\n    const items = await this.getActiveDiagnosticItems();\n    if (items.length === 0) {\n      sections.push(NO_DIAGNOSTICS_MESSAGE);\n    } else {\n      for (let i = 0; i < items.length; i++) {\n        if (i > 0) sections.push(`<hr>`);\n        const item = items[i]!;\n        if (this.pinnedError && item.html === this.pinnedError.html) {\n          sections.push(\n            `<div class=\"diagnostic-item pinned-message\">` +\n              `<em>This item is pinned on top.</em>` +\n              `</div>`\n          );\n        } else {\n          sections.push(`<div class=\"diagnostic-item\">${item.html}</div>`);\n        }\n      }\n    }\n\n    const fullHtml = sections.join(\"\");\n    if (fullHtml !== this.lastContent) {\n      webview.postMessage({ command: \"update-content\", html: fullHtml });\n      this.lastContent = fullHtml;\n    }\n  }\n}\n"
  },
  {
    "path": "apps/vscode-extension/src/supportedLanguageIds.ts",
    "content": "export const SUPPORTED_LANGUAGE_IDS = [\n  \"typescript\",\n  \"typescriptreact\",\n  \"javascript\",\n  \"javascriptreact\",\n  \"astro\",\n  \"svelte\",\n  \"vue\",\n  \"mdx\",\n  \"glimmer-js\",\n  \"glimmer-ts\",\n];\n"
  },
  {
    "path": "apps/vscode-extension/src/test/runTest.ts",
    "content": "import * as path from \"path\";\n\nimport { runTests } from \"@vscode/test-electron\";\n\nasync function main() {\n  try {\n    // The folder containing the Extension Manifest package.json\n    // Passed to `--extensionDevelopmentPath`\n    const extensionDevelopmentPath = path.resolve(__dirname, \"../../\");\n\n    // The path to test runner\n    // Passed to --extensionTestsPath\n    const extensionTestsPath = path.resolve(__dirname, \"./suite/index\");\n\n    // Download VS Code, unzip it and run the integration test\n    await runTests({\n      version: \"1.77.0\",\n      extensionDevelopmentPath,\n      extensionTestsPath,\n      launchArgs: [\"--disable-extensions\"],\n    });\n  } catch (err) {\n    console.error(\"Failed to run tests\");\n    process.exit(1);\n  }\n}\n\nmain();\n"
  },
  {
    "path": "apps/vscode-extension/src/test/suite/extension.test.ts",
    "content": "// You can import and use all API from the 'vscode' module\n// as well as import your extension to test it\nimport * as vscode from \"vscode\";\n\nsuite(\"Extension Test Suite\", () => {\n  /**\n   * The tests moved to the formatter package, I'm leaving\n   * this here for future tests to the VSCode extension\n   */\n  vscode.window.showInformationMessage(\"Start all tests.\");\n});\n"
  },
  {
    "path": "apps/vscode-extension/src/test/suite/index.ts",
    "content": "import * as path from \"path\";\nimport Mocha from \"mocha\";\nimport { glob } from \"glob\";\n\nexport function run(\n  testsRoot: string,\n  cb: (error: unknown | null, failures?: number) => void\n): void {\n  // Create the mocha test\n  const mocha = new Mocha({\n    ui: \"tdd\",\n    color: true,\n  });\n\n  glob(\"**/**.test.js\", { cwd: testsRoot })\n    .then((files) => {\n      // Add files to the test suite\n      files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));\n\n      try {\n        // Run the mocha test\n        mocha.run((failures) => {\n          cb(null, failures);\n        });\n      } catch (err) {\n        console.error(err);\n        cb(err);\n      }\n    })\n    .catch((err) => {\n      return cb(err);\n    });\n}\n"
  },
  {
    "path": "apps/vscode-extension/syntaxes/type.tmGrammar.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/RedCMD/TmLanguage-Syntax-Highlighter/main/vscode.tmLanguage.schema.json\",\n  \"name\": \"TypeScript Type\",\n  \"scopeName\": \"source.type\",\n  \"patterns\": [\n    {\n      \"include\": \"source.ts#type\"\n    },\n    {\n      \"include\": \"source.ts#directives\"\n    },\n    {\n      \"include\": \"source.ts#statements\"\n    },\n    {\n      \"include\": \"source.ts#shebang\"\n    }\n  ]\n}\n"
  },
  {
    "path": "apps/vscode-extension/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"module\": \"nodenext\",\n    \"target\": \"ES2022\",\n    \"rootDir\": \"src\",\n    \"outDir\": \"out\",\n    \"moduleResolution\": \"nodenext\"\n  },\n  \"include\": [\"src/**/*.ts\"],\n  \"references\": [\n    { \"path\": \"../../packages/utils\" },\n    { \"path\": \"../../packages/vscode-formatter\" }\n  ]\n}\n"
  },
  {
    "path": "apps/vscode-extension/webview/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <!-- @see https://code.visualstudio.com/api/extension-guides/webview#content-security-policy -->\n    <!-- NOTE: vscode classes, styles and acquireVsCodeApi are injected with inline scripts/styles, thus what is even the point of CSP headers? -->\n    <meta\n      http-equiv=\"Content-Security-Policy\"\n      data-csp-replace-content\n      content=\"default-src 'none'; script-src http://localhost:8080; style-src http://localhost:8080; font-src http://localhost:8080;\"\n    />\n    <title>Pretty TS Errors - Markdown Preview</title>\n    <!-- TODO: add script to copy vendor/codicon files from node_modules/ to ./vendor-->\n    <link\n      rel=\"stylesheet\"\n      href=\"./vendor/codicon.css\"\n      data-href-as-webview-uri\n    />\n    <link rel=\"stylesheet\" href=\"./style.css\" data-href-as-webview-uri />\n  </head>\n  <body>\n    <!-- #content will be filled with the markdown rendered content -->\n    <div id=\"content\"></div>\n    <script src=\"./index.js\" data-src-as-webview-uri></script>\n  </body>\n</html>\n"
  },
  {
    "path": "apps/vscode-extension/webview/index.js",
    "content": "// @ts-check\n\n// wrap this in IIFE because `vscode` should **NEVER** be leaked into the global scope\n// @see https://code.visualstudio.com/api/extension-guides/webview#passing-messages-from-a-webview-to-an-extension\nconst api = (function () {\n  // fallback logs to console, keep it for local development\n  const vscode =\n    typeof acquireVsCodeApi === \"function\"\n      ? acquireVsCodeApi()\n      : {\n          /**\n           * @param {unknown} message\n           */\n          postMessage(message) {\n            console.log(`message: `, message);\n          },\n        };\n  return {\n    /**\n     * Show a notification message in vscode\n     * @param {string} text\n     */\n    notify(text) {\n      vscode.postMessage({ command: \"notify\", text });\n    },\n  };\n})();\n\nconst $content = window.document.querySelector(\"#content\");\n\nwindow.addEventListener(\"message\", (event) => {\n  const message = event.data;\n  switch (message.command) {\n    case \"update-content\": {\n      if ($content) {\n        $content.innerHTML = message.html;\n      }\n    }\n  }\n});\n\nwindow.document.addEventListener(\"click\", (event) => {\n  const element = /** @type {HTMLElement} */ (event.target);\n  if (\n    element.tagName.toLowerCase() === \"button\" &&\n    element.hasAttribute(\"data-copy-content\")\n  ) {\n    handleCopyContentEvent(element);\n  }\n});\n\n/**\n *\n * @param {HTMLElement} element\n */\nfunction handleCopyContentEvent(element) {\n  const parent = element.parentElement;\n  if (parent?.classList.contains(\"code-container\")) {\n    const pre = parent.querySelector(\"pre\");\n    const code = pre?.querySelector(\"code\");\n    const content = code?.innerText;\n    if (content) {\n      copyToClipboard(content);\n    }\n  }\n}\n\n/**\n * Copy `text` to the user's clipboard\n * @param {string} text\n */\nasync function copyToClipboard(text) {\n  await navigator.clipboard.writeText(text);\n  api.notify(\"Copied type to clipboard!\");\n}\n"
  },
  {
    "path": "apps/vscode-extension/webview/style.css",
    "content": "body {\n  font-family:\n    -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", sans-serif;\n  line-height: 1.6;\n  max-width: 800px;\n  margin: 0 auto;\n  padding: 4px 0 12px;\n  color: var(--vscode-editor-foreground);\n}\n\n#content {\n  padding: 0 12px;\n}\n\n/* Tables from identSentences */\n#content table {\n  width: 100%;\n}\n\n#content table td:first-child {\n  white-space: nowrap;\n}\n\n#content table td:last-child {\n  width: 100%;\n  max-width: 0;\n  word-break: break-word;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  color: var(--vscode-foreground);\n  margin-top: 1.5em;\n  margin-bottom: 0.5em;\n}\n\npre {\n  background-color: var(--vscode-textCodeBlock-background) !important;\n  border: 1px solid var(--vscode-widget-border);\n  border-radius: 4px;\n  padding: 12px 16px;\n  overflow-x: auto;\n  margin: 0.75em 0;\n  line-height: 1.5;\n}\n\n/* Override Shiki's background to match VS Code */\npre code {\n  background-color: transparent !important;\n  border-radius: 3px;\n  padding: 0 !important;\n  font-weight: var(--vscode-editor-font-weight, normal);\n  font-size: var(--vscode-editor-font-size, medium);\n  font-family:\n    var(--vscode-editor-font-family), \"SF Mono\", Monaco, \"Cascadia Code\",\n    \"Roboto Mono\", Consolas, \"Courier New\", monospace;\n}\n\n.copy-button {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  background: var(--vscode-button-background);\n  color: var(--vscode-button-foreground);\n  border: none;\n  border-radius: 3px;\n  padding: 4px 8px;\n  cursor: pointer;\n  font-size: 12px;\n  opacity: 0;\n  transition: opacity 0.2s;\n}\n\n.code-container {\n  position: relative;\n  max-width: 100%;\n  overflow-x: auto;\n}\n\n.code-container pre {\n  margin: 0;\n}\n\n.code-container:hover .copy-button {\n  opacity: 1;\n}\n\n.copy-button:hover {\n  background: var(--vscode-button-hoverBackground);\n}\n\n#content a {\n  text-decoration: none;\n}\n\n#content hr {\n  border: none;\n  border-top: 1px solid var(--vscode-widget-border, rgba(128, 128, 128, 0.2));\n  margin: 12px 0;\n}\n\n.webview-panel .title-actions {\n  float: right;\n}\n\n.pinned-section {\n  margin: 0 -12px;\n  background-color: rgba(255, 235, 59, 0.04);\n  padding: 4px 12px;\n  margin-bottom: 8px;\n  overflow: hidden;\n}\n\n.pinned-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 4px;\n  font-size: 12px;\n}\n\n.pinned-label {\n  display: flex;\n  align-items: center;\n  gap: 4px;\n  color: var(--vscode-descriptionForeground);\n}\n\n.unpin-button {\n  color: var(--vscode-icon-foreground);\n  cursor: pointer;\n  padding: 2px;\n  line-height: 1;\n}\n\n.pinned-message {\n  color: var(--vscode-descriptionForeground);\n}\n"
  },
  {
    "path": "apps/vscode-extension/webview/vendor/codicon.css",
    "content": "/*---------------------------------------------------------------------------------------------\n *  Copyright (c) Microsoft Corporation. All rights reserved.\n *  Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\n@font-face {\n  font-family: \"codicon\";\n  font-display: block;\n  src: url(\"./codicon.ttf?eb10abb8b9291201e2c39eced5bd6993\") format(\"truetype\");\n}\n\n.codicon[class*=\"codicon-\"] {\n  font: normal normal normal 12px/1 codicon;\n  display: inline-block;\n  text-decoration: none;\n  text-rendering: auto;\n  text-align: center;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  user-select: none;\n  -webkit-user-select: none;\n  -ms-user-select: none;\n}\n\n/*---------------------\n *  Modifiers\n *-------------------*/\n\n@keyframes codicon-spin {\n  100% {\n    transform: rotate(360deg);\n  }\n}\n\n.codicon-sync.codicon-modifier-spin,\n.codicon-loading.codicon-modifier-spin,\n.codicon-gear.codicon-modifier-spin {\n  /* Use steps to throttle FPS to reduce CPU usage */\n  animation: codicon-spin 1.5s steps(30) infinite;\n}\n\n.codicon-modifier-disabled {\n  opacity: 0.5;\n}\n\n.codicon-modifier-hidden {\n  opacity: 0;\n}\n\n/* custom speed & easing for loading icon */\n.codicon-loading {\n  animation-duration: 1s !important;\n  animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67) !important;\n}\n\n/*---------------------\n *  Icons\n *-------------------*/\n.codicon-link-external:before {\n  content: \"\\eb14\";\n}\n.codicon-globe:before {\n  content: \"\\eb01\";\n}\n.codicon-layout-sidebar-left-dock:before {\n  content: \"\\ec4a\";\n}\n.codicon-copy:before {\n  content: \"\\ebcc\";\n}\n.codicon-go-to-file:before {\n  content: \"\\ea94\";\n}\n.codicon-indent:before {\n  content: \"\\ebf9\";\n}\n.codicon-debug:before {\n  content: \"\\ead8\";\n}\n.codicon-close:before {\n  content: \"\\ea76\";\n}\n.codicon-pinned:before {\n  content: \"\\eba0\";\n}\n"
  },
  {
    "path": "docs/hide-original-errors.md",
    "content": "To hide the original errors, display only the prettified ones, and make type blocks copyable, you can use the following hack:\n\n## The Hack\n\n1. Install the [Custom CSS and JS Loader](https://marketplace.visualstudio.com/items?itemName=be5invis.vscode-custom-css) extension from the VSCode marketplace.\n\n2. Follow the installation instructions provided by the extension, and use [this CSS file](./pretty-ts-errors-hack.css).\n\n## Why Do We Need This Hack?\n\n### Hiding Original Errors\n\nUnfortunately, VSCode doesn't currently support formatted diagnostics. Once it does, we'll be able to convert the extension to a TypeScript LSP Plugin that replaces the original error with the prettified version.  \nFor updates on this feature, follow [this issue](https://github.com/yoavbls/pretty-ts-errors/issues/3).\n\n### Making Type Blocks Copyable\n\nVSCode sanitizes and removes most CSS properties for security reasons. We've opened an [issue](https://github.com/microsoft/vscode/issues/180496) and submitted a [PR](https://github.com/microsoft/vscode/pull/180498) to allow the use of the `display` property. This would enable us to layout the types in a way that allows copying.\n\nUntil this change is approved, you can use the hack described above as a workaround.\n"
  },
  {
    "path": "docs/pretty-ts-errors-hack.css",
    "content": "/* Allow copying */\n.codicon-none {\n  user-select: text !important;\n  -webkit-user-select: text !important;\n}\n\n/* Hide errors */\ndiv.monaco-hover-content:has(.codicon-none) > .hover-row:first-child {\n  display: none !important;\n}\ndiv.monaco-hover-content:has([style=\"color:#f96363;\"])\n  > .hover-row:first-child {\n  display: none !important;\n}\n\n/* Change order */\n.monaco-hover .monaco-hover-content {\n  display: flex;\n  flex-direction: column;\n}\n.monaco-hover .monaco-hover-content .hover-row {\n  order: 2;\n}\n.monaco-hover .monaco-hover-content .hover-row:has(.codicon-none) {\n  order: 1;\n}\n.monaco-hover .monaco-hover-content .hover-row:has([style=\"color:#f96363;\"]) {\n  order: 1;\n}\n"
  },
  {
    "path": "docs/vscode-logs.md",
    "content": "# Instructions to find and export the VS Code logs for Pretty TypeScript Errors\n\n1. Open the output window to the `Pretty TypeScript Errors` channel\n   ![Navigate to the `Pretty TypeScript Errors` output channel](./vscode-logs/1-output-window.png)\n2. Set the log level of the `Pretty TypeScript Errors` output channel to `Trace`\n   ![Set the log level to `Trace`](vscode-logs/2-log-level.png)\n3. **Reproduce your bug or error**, this should generate verbose logging output.\n4. Either copy the output by selecting it or use one of the options in the menu shown:\n   ![Choose a method of copying or exporting the logs](vscode-logs/4-export-logs.png)\n5. Either paste the output or add the logfile to the GitHub issue\n   ![Add the otuput to the GitHub issue](vscode-logs/5-add-logs-to-gh.png)\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "// @ts-check\n\nimport eslint from \"@eslint/js\";\nimport tseslint from \"typescript-eslint\";\nimport globals from \"globals\";\n\nexport default tseslint.config(\n  eslint.configs.recommended,\n  ...tseslint.configs.strict,\n  ...tseslint.configs.stylistic,\n  {\n    ignores: [\n      \"apps/*/scripts/*\",\n      \"apps/*/dist/*\",\n      \"packages/*/scripts/*\",\n      \"packages/*/dist/*\",\n      \"examples/*\",\n      \".vscode-test/*\",\n    ],\n  },\n  {\n    languageOptions: {\n      parser: tseslint.parser,\n      parserOptions: {\n        ecmaVersion: \"latest\",\n        sourceType: \"module\",\n      },\n      globals: {\n        ...globals.node,\n        /**\n         * @see https://www.npmjs.com/package/@types/vscode-webview\n         */\n        acquireVsCodeApi: false,\n        window: false,\n      },\n    },\n    plugins: {\n      \"@typescript-eslint\": tseslint.plugin,\n    },\n    rules: {\n      \"@typescript-eslint/no-unused-vars\": \"off\",\n      \"@typescript-eslint/no-non-null-assertion\": \"off\",\n      curly: \"warn\",\n      eqeqeq: [\"warn\", { smart: true }],\n      \"no-throw-literal\": \"warn\",\n      semi: \"off\",\n    },\n  }\n);\n"
  },
  {
    "path": "examples/errors.js",
    "content": "// @ts-check\n\n/**\n * @typedef {Object} Person\n * @property {string} name\n * @property {number} age\n * @property {Object} address\n * @property {string} address.street\n * @property {string} address.city\n * @property {string} address.country\n */\n\n/**\n * @type {Person}\n */\nconst john = {\n  name: \"John Doe\",\n  age: 30,\n  address: {\n    street: \"123 Main St\",\n    city: \"New York\",\n  },\n};\n\n/**\n * @typedef {Function} GetUserFunction\n * @returns {{ user: { name: string, email: string, age: number } }}\n */\n\nconst getPerson = () => ({\n  person: {\n    username: \"usr\",\n    email: \"usr@usr.io\",\n  },\n});\n\n/**\n * @typedef {Object} JSAnimal\n * @property {string} name\n * @property {number} age\n */\n\n/**\n * @template {JSAnimal} T\n * @param {T} animal\n * @returns\n */\nfunction run(animal) {\n  return animal;\n}\n\nrun({ firstName: \"John\", weight: 20 });\n\n/**\n * @typedef {Object} MyError\n * @property {number} code\n */\n\ntry {\n  // ...\n} catch (/** @type {MyError} */ error) {\n  console.log(error.code);\n}\n\nexport {};\n"
  },
  {
    "path": "examples/errors.ts",
    "content": "interface Person {\n  name: string;\n  age: number;\n  address: {\n    street: string;\n    city: string;\n    country: string;\n  };\n}\n\nconst john: Person = {\n  name: \"John Doe\",\n  age: 30,\n  address: {\n    street: \"123 Main St\",\n    city: \"New York\",\n  },\n};\n\ntype GetUserFunction = () => {\n  user: {\n    name: string;\n    email: `${string}@${string}.${string}`;\n    age: number;\n  };\n};\n\nconst getPerson: GetUserFunction = () => ({\n  person: {\n    username: \"usr\",\n    email: \"usr@usr.io\",\n  },\n});\n\ninterface Animal {\n  name: string;\n  age: number;\n}\n\nfunction run<T extends Animal>(animal: T) {\n  return animal;\n}\n\nrun({ firstName: \"John\", weight: 20 });\n\ntype MyError = {\n  code: number;\n};\n\ntry {\n  // ...\n} catch (error: MyError) {\n  console.log(error.code);\n}\n\nexport {};\n"
  },
  {
    "path": "examples/errors.vue",
    "content": "<script setup lang=\"ts\">\nimport { RouterView } from \"vue-router\";\n\nconst x = [1, 2, 3, \"4\"];\n\nfunction y(param: number[]) {}\n\ny(x);\n</script>\n<template>\n  <RouterView />\n</template>\n"
  },
  {
    "path": "examples/examples.type",
    "content": "{\n  fistName: string;\n  lastName: string;\n  age: number;  \n} | undefined | null;\n\n{\n  x: string;\n  y: number;\n}[]"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"turbo run build\",\n    \"dev\": \"turbo run dev\",\n    \"lint\": \"turbo run lint\",\n    \"format\": \"prettier --write \\\"**/*.{ts,tsx,js,mjs,cjs,json,md,yml,yaml,css,html}\\\"\",\n    \"format:check\": \"prettier --check \\\"**/*.{ts,tsx,js,mjs,cjs,json,md,yml,yaml,css,html}\\\"\"\n  },\n  \"engines\": {\n    \"node\": \">=20\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.15.0\",\n    \"@types/node\": \"^20.19.21\",\n    \"eslint\": \"^9.15.0\",\n    \"globals\": \"^16.4.0\",\n    \"prettier\": \"^3.6.2\",\n    \"turbo\": \"^2.1.3\",\n    \"typescript\": \"^5.7.2\",\n    \"typescript-eslint\": \"^8.15.0\"\n  },\n  \"name\": \"pretty-ts-errors-mono\",\n  \"packageManager\": \"npm@10.0.0\",\n  \"workspaces\": [\n    \"packages/*\",\n    \"apps/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/formatter/README.md",
    "content": "# Pretty TypeScript Error formatter\n\nThe formatting package of [pretty-ts-errors](https://github.com/yoavbls/pretty-ts-errors)\n\n# Usage\n\n```typescript\nimport { createErrorMessagePrettifier } from \"@pretty-ts-errors/formatter\";\n\nfunction codeBlock(code: string, language?: string, multiLine?: boolean) {\n  return `\\`\\`\\`${language}\n${code}\n\\`\\`\\`\n`;\n}\n\nconst prettifyErrorMessage = createErrorMessagePrettifier(codeBlock);\n\nprettifyErrorMessage(`Type 'string' is not assignable to type 'number'.`);\n```\n"
  },
  {
    "path": "packages/formatter/package.json",
    "content": "{\n  \"name\": \"@pretty-ts-errors/formatter\",\n  \"version\": \"0.1.8\",\n  \"description\": \"Pretty TypeScript Errors Formatter\",\n  \"files\": [\n    \"src/**\",\n    \"dist/**\"\n  ],\n  \"main\": \"./dist/index.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"tsdown src/index.ts --format cjs,esm --dts\",\n    \"dev\": \"tsdown src/index.ts --format cjs,esm --dts --watch\",\n    \"lint\": \"tsc -p . --noEmit\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest\",\n    \"publish\": \"npm run build && npm publish --access public\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/yoavbls/pretty-ts-errors.git\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"errors\"\n  ],\n  \"author\": \"yoavbls\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/yoavbls/pretty-ts-errors/issues\"\n  },\n  \"homepage\": \"https://github.com/yoavbls/pretty-ts-errors#readme\",\n  \"devDependencies\": {\n    \"tsdown\": \"^0.15.6\",\n    \"vitest\": \"^3.2.4\"\n  },\n  \"dependencies\": {\n    \"@pretty-ts-errors/utils\": \"*\",\n    \"prettier\": \"^3.6.2\"\n  }\n}\n"
  },
  {
    "path": "packages/formatter/src/addMissingParentheses.ts",
    "content": "import { has, invert, objectKeys } from \"@pretty-ts-errors/utils\";\n\nconst parentheses = {\n  \"(\": \")\",\n  \"{\": \"}\",\n  \"[\": \"]\",\n} as const;\n\nconst openParentheses = objectKeys(parentheses);\nconst closeParentheses = Object.values(parentheses);\n\nexport function addMissingParentheses(type: string): string {\n  const openStack: (typeof openParentheses)[number][] = [];\n  const missingClosingChars: string[] = [];\n\n  for (const char of type) {\n    if (has(openParentheses, char)) {\n      openStack.push(char);\n    } else if (has(closeParentheses, char)) {\n      const lastOpen = openStack[openStack.length - 1];\n      if (lastOpen === undefined || parentheses[lastOpen] !== char) {\n        // Add the correct opening character before the current closing character\n        openStack.push(invert(parentheses)[char]);\n      } else {\n        openStack.pop();\n      }\n    }\n  }\n\n  // Add the missing closing characters at the end of the string\n  while (openStack.length > 0) {\n    const openChar = openStack.pop()!;\n    const closingChar = parentheses[openChar];\n    missingClosingChars.push(closingChar);\n  }\n\n  let validType = type;\n\n  // Close the last string if it's not closed\n  const quoteMatches = validType.match(/['\"]/g);\n  if (quoteMatches) {\n    const lastQuote = quoteMatches[quoteMatches.length - 1];\n    if (quoteMatches.length % 2 === 1) {\n      validType += `...${lastQuote}`;\n    }\n  }\n\n  if (validType.endsWith(\":\")) {\n    validType += \"...\";\n  }\n\n  validType = (validType + \"\\n...\" + missingClosingChars.join(\"\")).replace(\n    // Change (param: ...) to (param) => __RETURN_TYPE__ if needed\n    /(\\([a-zA-Z0-9]*:[^)]*\\))/,\n    (p1) => `${p1} => ...`\n  );\n\n  return validType;\n}\n"
  },
  {
    "path": "packages/formatter/src/errorMessagePrettifier.ts",
    "content": "import { formatTypeBlock } from \"./formatTypeBlock\";\n\nexport type CodeBlockFn = (\n  code: string,\n  language?: string,\n  multiLine?: boolean\n) => string;\n\nexport function createErrorMessagePrettifier(\n  codeBlock: CodeBlockFn\n): (message: string) => Promise<string> {\n  return async (message: string) => {\n    const rules = await getRules(codeBlock);\n    let output = message;\n\n    for (const { pattern, replacer } of rules) {\n      let result = \"\";\n      let lastIndex = 0;\n      for (const match of output.matchAll(pattern)) {\n        const [fullMatch, ...captures] = match;\n        const matchIndex = match.index ?? 0;\n        result += output.slice(lastIndex, matchIndex);\n        result += await replacer(...captures);\n        lastIndex = matchIndex + fullMatch.length;\n      }\n      result += output.slice(lastIndex);\n      output = result;\n    }\n    return output;\n  };\n}\n\ntype Rule = {\n  pattern: RegExp;\n  replacer: (...args: any[]) => string | Promise<string>;\n};\n\nasync function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {\n  const formatTypeScriptBlock = (code: string) => codeBlock(code, \"typescript\");\n\n  const formatSimpleTypeBlock = (code: string) => codeBlock(code, \"type\");\n\n  const formatTypeOrModuleBlock = (prefix: string, code: string) =>\n    formatTypeBlock(\n      prefix,\n      [\"module\", \"file\", \"file name\"].includes(prefix.toLowerCase())\n        ? `\"${code}\"`\n        : code,\n      codeBlock\n    );\n\n  return [\n    {\n      pattern: /(?:\\s)'\"(.*?)(?<!\\\\)\"'(?:\\s|:|.|$)/g,\n      replacer: async (p1: string) => formatTypeBlock(\"\", `\"${p1}\"`, codeBlock),\n    },\n    {\n      pattern: /['“](declare module )['”](.*)['“];['”]/g,\n      replacer: (p1: string, p2: string) =>\n        formatTypeScriptBlock(`${p1} \"${p2}\"`),\n    },\n    {\n      pattern:\n        /(is missing the following properties from type\\s?)'(.*)': ((?:#?\\w+, )*(?:(?!and)\\w+)?)/g,\n      replacer: async (pre: string, type: string, post: string) => {\n        const formattedType = await formatTypeBlock(\"\", type, codeBlock);\n        const list = post\n          .split(\", \")\n          .filter(Boolean)\n          .map((prop: string) => `<li>${prop}</li>`)\n          .join(\"\");\n        return `${pre}${formattedType}: <ul>${list}</ul>`;\n      },\n    },\n    {\n      pattern: /(types) ['“](.*?)['”] and ['“](.*?)['”][.]?/gi,\n      replacer: async (p1: string, p2: string, p3: string) => {\n        const [left, right] = await Promise.all([\n          formatTypeBlock(p1, p2, codeBlock),\n          formatTypeBlock(\"\", p3, codeBlock),\n        ]);\n        return `${left} and ${right}`;\n      },\n    },\n    {\n      pattern: /type annotation must be ['“](.*?)['”] or ['“](.*?)['”][.]?/gi,\n      replacer: async (p1: string, p2: string, p3: string | number) => {\n        if (typeof p3 === \"string\") {\n          const [left, right] = await Promise.all([\n            formatTypeBlock(p1, p2, codeBlock),\n            formatTypeBlock(\"\", p3, codeBlock),\n          ]);\n          return `${left} or ${right}`;\n        }\n        const [left, right] = await Promise.all([\n          formatTypeBlock(\"\", p1, codeBlock),\n          formatTypeBlock(\"\", p2, codeBlock),\n        ]);\n        return `${left} or ${right}`;\n      },\n    },\n    {\n      pattern: /(Overload \\d of \\d), ['“](.*?)['”], /gi,\n      replacer: async (p1: string, p2: string) =>\n        `${p1}${await formatTypeBlock(\"\", p2, codeBlock)}`,\n    },\n    {\n      pattern: /^['“]\"[^\"]*\"['”]$/g,\n      replacer: formatTypeScriptBlock,\n    },\n    {\n      pattern: /(module )'([^\"]*?)'/gi,\n      replacer: (p1: string, p2: string) => `${p1}\"${p2}\"`,\n    },\n    {\n      pattern:\n        /(module|file|file name|imported via) ['\"“](.*?)['\"“](?=[\\s(.|,]|$)/gi,\n      replacer: async (p1: string, p2: string) =>\n        formatTypeBlock(p1, `\"${p2}\"`, codeBlock),\n    },\n    {\n      pattern:\n        /(type|type alias|interface|module|file|file name|class|method's|subtype of constraint) ['“](.*?)['“](?=[\\s(.|,)]|$)/gi,\n      replacer: (p1: string, p2: string) => formatTypeOrModuleBlock(p1, p2),\n    },\n    {\n      pattern:\n        /(.*)['“]([^>]*)['”] (type|interface|return type|file|module|is (not )?assignable)/gi,\n      replacer: async (p1: string, p2: string, p3: string) =>\n        `${p1}${await formatTypeOrModuleBlock(\"\", p2)} ${p3}`,\n    },\n    {\n      pattern:\n        /['“]((void|null|undefined|any|boolean|string|number|bigint|symbol)(\\[\\])?)['”]/g,\n      replacer: formatSimpleTypeBlock,\n    },\n    {\n      pattern:\n        /['“](import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?.*?)['”]/g,\n      replacer: (p1: string, p2: string) => formatTypeScriptBlock(`${p1}${p2}`),\n    },\n    {\n      pattern: /(return|operator) ['“](.*?)['”]/gi,\n      replacer: (p1: string, p2: string) =>\n        `${p1} ${formatTypeScriptBlock(p2)}`,\n    },\n    {\n      pattern: /(?<!\\w)'((?:(?![\"]).)*?)'(?!\\w)/g,\n      replacer: (p1: string) => ` ${codeBlock(p1)} `,\n    },\n  ];\n}\n"
  },
  {
    "path": "packages/formatter/src/formatTypeBlock.ts",
    "content": "import { addMissingParentheses } from \"./addMissingParentheses\";\nimport { formatTypeWithPrettier } from \"./formatTypeWithPrettier\";\n\nexport async function formatTypeBlock(\n  prefix: string,\n  type: string,\n  codeBlock: (code: string, language?: string, multiLine?: boolean) => string\n) {\n  // Return a simple code block if it's just a parenthesis\n  if (type.match(/^(\\[\\]|\\{\\})$/)) {\n    return `${prefix} ${codeBlock(type)}`;\n  }\n\n  if (\n    // Skip formatting if it's a simple type\n    type.match(\n      /^((void|null|undefined|any|number|string|bigint|symbol|readonly|typeof)(\\[\\])?)$/\n    )\n  ) {\n    return `${prefix} ${codeBlock(type, \"type\")}`;\n  }\n\n  const formattedType = await formatType(type);\n\n  if (formattedType.includes(\"\\n\")) {\n    return `${prefix}: ${codeBlock(formattedType, \"type\", true)}`;\n  } else {\n    return `${prefix} ${codeBlock(formattedType, \"type\")}`;\n  }\n}\n/**\n * Try to format type with prettier\n */\nexport async function formatType(\n  type: string,\n  options?: { throwOnError?: boolean }\n) {\n  try {\n    // Wrap type with valid statement, format it and extract the type back\n    return convertToOriginalType(\n      await formatTypeWithPrettier(convertToValidType(type))\n    );\n  } catch (e) {\n    if (options?.throwOnError) {\n      throw e;\n    }\n    return type;\n  }\n}\n\nconst convertToValidType = (type: string) =>\n  `type x = ${type\n    // Add missing parentheses when the type ends with \"...\"\"\n    .replace(/^(.*)\\.\\.\\.$/, (_, p1) => addMissingParentheses(p1))\n    // Replace single parameter function destructuring because it's not a valid type\n    // .replaceAll(/\\((\\{.*\\})\\:/g, (_, p1) => `(param: /* ${p1} */`)\n    // Change `(...): return` which is invalid to `(...) => return`\n    .replace(/^(\\(.*\\)): /, (_, p1) => `${p1} =>`)\n    .replaceAll(/... (\\d{0,}) more .../g, (_, p1) => `___${p1}MORE___`)\n    .replaceAll(/... (\\d{0,}) more ...;/g, (_, p1) => `___MORE___: ${p1};`)\n    .replaceAll(\"...;\", \"___KEY___: ___THREE_DOTS___;\")\n    .replaceAll(\"...\", \"__THREE_DOTS__\")};`;\n\nconst convertToOriginalType = (type: string) =>\n  type\n    .replaceAll(\"___KEY___: ___THREE_DOTS___\", \"...;\")\n    .replaceAll(\"__THREE_DOTS__\", \"...\")\n    .replaceAll(/___MORE___: (\\d{0,});/g, (_, p1) => `... ${p1} more ...;`)\n    .replaceAll(/___(\\d{0,})MORE___/g, (_, p1) => `... ${p1} more ...`)\n    .replaceAll(/'([^']+)'(?=\\s*:)/g, '\"$1\"')\n    .replaceAll(/... (\\d{0,}) more .../g, (_, p1) => `/* ${p1} more */`) // ... x more ... not shown sell\n    // .replaceAll(/\\(param\\: \\/\\* (\\{ .* \\}) \\*\\//g, (_, p1) => `(${p1}: `)\n    .replace(/^type x =[ ]?([\\s\\S]*?);?$/g, \"$1\")\n    .trim();\n"
  },
  {
    "path": "packages/formatter/src/formatTypeWithPrettier.ts",
    "content": "import { format } from \"prettier/standalone\";\nimport * as parserEstree from \"prettier/plugins/estree\";\nimport * as parserTypescript from \"prettier/plugins/typescript\";\n\nexport async function formatTypeWithPrettier(text: string) {\n  return format(text, {\n    plugins: [parserTypescript, parserEstree],\n    parser: \"typescript\",\n    printWidth: 60,\n    arrowParens: \"avoid\",\n    semi: false,\n    singleQuote: false,\n  });\n}\n"
  },
  {
    "path": "packages/formatter/src/index.ts",
    "content": "export {\n  createErrorMessagePrettifier,\n  type CodeBlockFn,\n} from \"./errorMessagePrettifier\";\n"
  },
  {
    "path": "packages/formatter/test/errorMessageMocks.ts",
    "content": "/**\n * This file contains mocks of error messages, only some of them\n * are used in tests but all of them can be used to test and debug\n * the formatting visually, you can try to select them on debug and check the hover.\n */\n\nimport { d } from \"@pretty-ts-errors/utils\";\n\nexport const errorWithSpecialCharsInObjectKeys = d`\nType 'string' is not assignable to type '{ 'abc*bc': string; }'.\n`;\n\nexport const errorWithDashInObjectKeys = d`\nType '{ person: { 'first-name': string; }; }' is not assignable to type 'string'.\n`;\n\n/**\n * Formatting error from this issue: https://github.com/yoavbls/pretty-ts-errors/issues/20\n */\nexport const errorWithMethodsWordInIt = d`\nThe 'this' context of type 'ElementHandle<Node>' is not assignable to method's 'this' of type 'ElementHandle<Element>'.\n  Type 'Node' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 114 more.\n`;\n\nexport const errorWithParamsDestructuring = d`\nArgument of type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' is not assignable to parameter of type 'VTableConfig'.\n  Property 'data' is missing in type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' but required in type 'VTableConfig'.\n`;\n\nexport const errorWithLongType = d`\nProperty 'isFlying' is missing in type '{ animal: { __typename?: \"Animal\" | undefined; id: string; name: string; age: number; isAlived: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }' but required in type '{ animal: { __typename?: \"Animal\" | undefined; id: string; name: string; age: number; isAlived: boolean; isFlying: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }'.\n`;\n\nexport const errorWithTruncatedType2 = d`\nType '{ '!top': string[]; 'xsl:declaration': { attrs: { 'default-collation': null; 'exclude-result-prefixes': null; 'extension-element-prefixes': null; 'use-when': null; 'xpath-default-namespace': null; }; }; 'xsl:instruction': { ...; }; ... 49 more ...; 'xsl:literal-result-element': {}; }' is missing the following properties from type 'GraphQLSchema': description, extensions, astNode, extensionASTNodes, and 21 more.\n`;\n\nexport const variableNotUsedEror = d`\n'a' is declared but its value is never read.\n`;\n\nexport const errorWithSimpleIndentations = d`\nType '(newIds: number[]) => void' is not assignable to type '(selectedId: string[]) => void'.\n  Types of parameters 'newIds' and 'selectedId' are incompatible.\n    Type 'string[]' is not assignable to type 'number[]'.\n      Type 'string' is not assignable to type 'number'.\n`;\n\nexport const errorWithComma = d`\nArgument of type '{ filters: Filters; } & T' is not assignable to parameter of type 'T & F'.\n  Type '{ filters: Filters; } & T' is not assignable to type 'F'.\n    '{ filters: Filters; } & T' is assignable to the constraint of type 'F', but 'F' could be instantiated with a different subtype of constraint '{ filters: Filters; }'.\n`;\n\nexport const missingPropertyError =\n  \"\\\nProperty 'user' is missing in type '{ person: { username: string; email: string; }; }' but required in type '{ user: { name: string; email: `${string}@${string}.${string}`; age: number; }; }'.\\\n\";\n\nexport const missingReactPropsError = d`\nType '{ style: { backgroundColor: string; }; }' is not assignable to type 'DropDownPickerProps<Object>'.\n  Type '{ style: { backgroundColor: string; }; }' is not assignable to type 'DropDownPickerMultipleProps<Object> & DropDownPickerBaseProps<Object›'.\n    Type '{ style: { backgroundColor: string; }; }' is missing the following properties from type 'DropDownPickerMultipleProps<Object>': multiple, setValue, value\n`;\n\nexport const leftSideAritmeticError = d`\nThe left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.\n`;\n\nexport const theosError = d`\nExported variable 'uploadRouter' has or is using name 'Uploader' from external module \"/Users/theo/Code/Work/filething/packages/uploadthing/dist/types-dbaf1b46\" but cannot be named.\n`;\n\nexport const ts1378Error = d`\nTop-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.\n`;\n\nexport const ts2304Error = d`\nCannot find name 'varname'.\n`;\n\nexport const ts2305Error = d`\nModule '\"@pretty-ts-errors/formatter\"' has no exported member 'values'.\n`;\n\nexport const ts2307Error = d`\nCannot find module 'events' or its corresponding type declarations.\n`;\n\nexport const ts1360Error = d`\nProperty 'a' is missing in type '{ b: { name: string; icon: undefined; }; c: { name: string; icon: undefined; }; d: { name: string; icon: undefined; }; e: { name: string; icon: undefined; }; f: { ...; }; g: { ...; }; h:...' but required in type '{a: {name: string; icon: undefined}}'.\n`;\n\nexport const errorWithStringChars = d`\nType '\"' 'Oh no\"' is not assignable to type '\"'  'Oh n\\\"o\\\"'   \"'.\n`;\n\nexport const ts2322ErrorWithPrivateProperty = d`\nType 'Ref<{ name: string; readonly type: \"json\"; mm: <T extends Convertible = Convertible>(px: T) => T; px: <T extends Convertible = Convertible>(mm: T) => T; ... 18 more ...; toJson: () => string; }>' is not assignable to type 'Ref<MpcdiConfiguration>'.\n  Type '{ name: string; readonly type: \"json\"; mm: <T extends Convertible = Convertible>(px: T) => T; px: <T extends Convertible = Convertible>(mm: T) => T; ... 18 more ...; toJson: () => string; }' is missing the following properties from type 'MpcdiConfiguration': ratio, #overlaps, download\n`;\n\nexport const ts4113Error = d`\nThis member cannot have an 'override' modifier because it is not declared in the base class 'A<T[\"nested\"]>'.\n`;\n\nexport const ts4117Error = d`\nThis member cannot have an 'override' modifier because it is not declared in the base class 'A<T[\"nested\"]>'. Did you mean 'testA'?\n`;\n"
  },
  {
    "path": "packages/formatter/test/formatter.vitest.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport {\n  createErrorMessagePrettifier,\n  type CodeBlockFn,\n} from \"../src/errorMessagePrettifier\";\nimport { addMissingParentheses } from \"../src/addMissingParentheses\";\nimport { formatType } from \"../src/formatTypeBlock\";\nimport { d } from \"@pretty-ts-errors/utils\";\nimport {\n  errorWithDashInObjectKeys,\n  errorWithSpecialCharsInObjectKeys,\n} from \"./errorMessageMocks\";\nimport * as errorMessageMocks from \"./errorMessageMocks\";\n\n// Simple stub that marks code blocks without any rendering logic\nconst stubCodeBlock: CodeBlockFn = (code, language, multiLine) => {\n  if (multiLine) return `\\n\\`\\`\\`${language}\\n${code}\\n\\`\\`\\`\\n`;\n  return `\\`${code}\\``;\n};\n\nconst prettifyErrorMessage = createErrorMessagePrettifier(stubCodeBlock);\n\ndescribe(\"formatter\", () => {\n  it(\"adds missing parentheses\", () => {\n    expect(addMissingParentheses(\"Hello, {world! [This] is a (test.\")).toBe(\n      \"Hello, {world! [This] is a (test.\\n...)}\"\n    );\n  });\n\n  it(\"formats Special characters in object keys\", async () => {\n    expect(await prettifyErrorMessage(errorWithSpecialCharsInObjectKeys)).toBe(\n      'Type `string` is not assignable to type `{ \"abc*bc\": string }`.'\n    );\n  });\n\n  it(\"formats method's word in the error\", async () => {\n    expect(await prettifyErrorMessage(errorWithDashInObjectKeys)).toBe(\n      'Type `{ person: { \"first-name\": string } }` is not assignable to type `string`.'\n    );\n  });\n\n  it(\"prettifies type with params destructuring\", async () => {\n    await expect(\n      formatType(\n        d` { $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }\n        `,\n        { throwOnError: true }\n      )\n    ).resolves.toBeTypeOf(\"string\");\n  });\n\n  it(\"prettifies truncated type\", async () => {\n    await expect(\n      formatType(\n        d` { b: { name: string; icon: undefined; }; c: { name: string; icon: undefined; }; d: { name: string; icon: undefined; }; e: { name: string; icon: undefined; }; f: { ...; }; g: { ...; }; h:...`,\n        { throwOnError: true }\n      )\n    ).resolves.toBeTypeOf(\"string\");\n  });\n\n  it.each(Object.entries(errorMessageMocks))(\n    \"prettifies mock error message: %s\",\n    async (_name, message) => {\n      await expect(prettifyErrorMessage(message)).resolves.toBeTypeOf(\"string\");\n    }\n  );\n});\n"
  },
  {
    "path": "packages/formatter/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"references\": [{ \"path\": \"../utils\" }]\n}\n"
  },
  {
    "path": "packages/formatter/vitest.config.ts",
    "content": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  test: {\n    environment: \"node\",\n    globals: true,\n    include: [\"test/**/*.vitest.{ts,tsx}\", \"test/**/*.spec.{ts,tsx}\"],\n  },\n});\n"
  },
  {
    "path": "packages/utils/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"@pretty-ts-errors/utils\",\n  \"files\": [\n    \"dist/**\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"tsc -p .\"\n  },\n  \"devDependencies\": {},\n  \"dependencies\": {\n    \"ts-dedent\": \"^2.2.0\"\n  }\n}\n"
  },
  {
    "path": "packages/utils/src/index.ts",
    "content": "import dedent from \"ts-dedent\";\n\nexport function objectKeys<T extends Record<string, unknown>>(\n  obj: T\n): (keyof T & string)[] {\n  return Object.keys(obj) as (keyof T & string)[];\n}\n\nexport function invert<T extends Record<string, string>>(\n  obj: T\n): Partial<{\n  [K in T[keyof T]]: { [P in keyof T]: K extends T[P] ? P : never }[keyof T];\n}> {\n  const result: Partial<{\n    [K in T[keyof T]]: { [P in keyof T]: K extends T[P] ? P : never }[keyof T];\n  }> = {};\n  for (const key in obj) {\n    const value = obj[key];\n    if (value !== undefined) {\n      result[value] = key;\n    }\n  }\n  return result;\n}\n\n/**\n * d stands for dedent.\n * it allow us to indent html in template literals without affecting the output\n */\nexport const d = dedent;\n\n/**\n * Check if an array contains a string.\n * Type guard the string if it does.\n */\nexport const has = (\n  array: unknown[],\n  item: string\n): item is Extract<(typeof array)[number], string> => array.includes(item);\n"
  },
  {
    "path": "packages/utils/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/vscode-formatter/README.md",
    "content": "# Pretty TypeScript Errors - Formatter for VSCode hovers\n"
  },
  {
    "path": "packages/vscode-formatter/package.json",
    "content": "{\n  \"name\": \"@pretty-ts-errors/vscode-formatter\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Pretty TypeScript Errors Formatter for VSCode hovers\",\n  \"files\": [\n    \"src/**\",\n    \"dist/**\"\n  ],\n  \"main\": \"./dist/index.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"tsdown  src/index.ts --format cjs,esm --dts\",\n    \"dev\": \"tsdown src/index.ts --format cjs,esm --dts --watch\",\n    \"lint\": \"tsc -p . --noEmit\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest\",\n    \"publish\": \"npm run build && npm publish --access public\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/yoavbls/pretty-ts-errors.git\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"errors\"\n  ],\n  \"author\": \"yoavbls\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/yoavbls/pretty-ts-errors/issues\"\n  },\n  \"homepage\": \"https://github.com/yoavbls/pretty-ts-errors#readme\",\n  \"devDependencies\": {\n    \"tsdown\": \"^0.15.6\",\n    \"vitest\": \"^3.2.4\"\n  },\n  \"dependencies\": {\n    \"@pretty-ts-errors/formatter\": \"*\",\n    \"@pretty-ts-errors/utils\": \"*\",\n    \"lz-string\": \"^1.5.0\",\n    \"vscode-languageserver-types\": \"^3.17.5\",\n    \"vscode-uri\": \"^3.1.0\"\n  }\n}\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/actions.ts",
    "content": "import { compressToEncodedURIComponent } from \"lz-string\";\nimport { Diagnostic, Range } from \"vscode-languageserver-types\";\nimport { d } from \"@pretty-ts-errors/utils\";\n\nexport const divider = `<span class=\"divider\">|</span>`;\n\nexport const errorCodeExplanationLink = (errorCode: Diagnostic[\"code\"]) =>\n  d /*html*/ `\n    <a title=\"See detailed explanation\" href=\"https://typescript.tv/errors/ts${errorCode}\">\n      <span class=\"codicon codicon-link-external\">\n      </span>\n    </a>`;\n\nexport const showErrorInSidebarLink = (range: Range, message?: string) => {\n  const args = encodeURIComponent(\n    JSON.stringify(message != null ? [range, message] : [range])\n  );\n  return d /*html*/ `\n    <a title=\"Show error in sidebar\" href=\"command:prettyTsErrors.showErrorInSidebar?${args}\">\n      <span class=\"codicon codicon-layout-sidebar-left-dock\">\n      </span>\n    </a>`;\n};\n\nexport const pinErrorLink = (range: Range, message?: string) => {\n  const args = encodeURIComponent(\n    JSON.stringify(message != null ? [range, message] : [range])\n  );\n  return d /*html*/ `\n    <a title=\"Pin error\" href=\"command:prettyTsErrors.pinError?${args}\">\n      <span class=\"codicon codicon-pinned\">\n      </span>\n    </a>`;\n};\n\nexport const copyErrorLink = (message: Diagnostic[\"message\"]) => {\n  const args = encodeURIComponent(JSON.stringify(message));\n  return d /*html*/ `\n    <a title=\"Copy error to clipboard\" href=\"command:prettyTsErrors.copyError?${args}\">\n      <span class=\"codicon codicon-copy\">\n      </span>\n    </a>`;\n};\n\nexport const errorMessageTranslationLink = (message: Diagnostic[\"message\"]) => {\n  const encodedMessage = compressToEncodedURIComponent(message);\n  return d /*html*/ `\n    <a title=\"See translation\" href=\"https://ts-error-translator.vercel.app/?error=${encodedMessage}\">\n      <span class=\"codicon codicon-globe\">\n      </span>\n    </a>`;\n};\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/errorTitle.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\nimport { Diagnostic } from \"vscode-languageserver-types\";\n\nexport const errorTitle = (\n  code: Diagnostic[\"code\"],\n  actions: string,\n  suffix = \"\"\n) => d /*html*/ `\n    <span style=\"color:#f96363;\">⚠ Error </span>${\n      typeof code === \"number\"\n        ? d /*html*/ `\n            <span style=\"color:#5f5f5f;\">\n            (TS${code})\n            <span class=\"title-actions\">\n            ${actions}\n            </span>\n            </span>\n          `\n        : \"\"\n    }\n    <br>\n    ${suffix}\n`;\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/hoverCodeBlock.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\nimport { miniLine } from \"./miniLine\";\nimport { spanBreak } from \"./spanBreak\";\nimport { CodeBlockFn } from \"@pretty-ts-errors/formatter\";\nimport { plainCodeBlock } from \"./plainCodeBlock\";\n\n/**\n * @returns markdown string that will be rendered as a code block (`supportHtml` required)\n * We're using codicon here since it's the only thing that can be `inline-block`\n * and have a background color in hovers due to strict sanitization of markdown on\n * VSCode [code](https://github.com/microsoft/vscode/blob/735aff6d962db49423e02c2344e60d418273ae39/src/vs/base/browser/markdownRenderer.ts#L372)\n */\nconst codeBlock = (code: string, language: string) =>\n  spanBreak(d /*html*/ `\n  <span class=\"codicon codicon-none\" style=\"background-color:var(--vscode-textCodeBlock-background);\">\n\n    \\`\\`\\`${language}\n    ${code}\n    \\`\\`\\`\n\n  </span>\n`);\n\nconst inlineHoverCodeBlock = (code: string, language: string) =>\n  codeBlock(` ${code} `, language);\n\nconst multiLineHoverCodeBlock = (code: string, language: string) => {\n  const codeLines = code.split(\"\\n\");\n  //this line is finding the longest line\n  const maxLineChars = codeLines.reduce(\n    (acc, curr) => (curr.length > acc ? curr.length : acc),\n    0\n  );\n  // codicon class align the code to the center, so we must pad it with spaces\n  const paddedCode = codeLines\n    .map((line) => line.padEnd(maxLineChars + 2))\n    .join(\"\\n\");\n\n  return d /*html*/ `    \n    ${miniLine}\n    ${codeBlock(paddedCode, language)}\n    ${miniLine}\n    `;\n};\n\nexport const hoverCodeBlock: CodeBlockFn = (code, language, multiLine) => {\n  if (!language) {\n    return plainCodeBlock(code);\n  }\n  if (multiLine) {\n    return multiLineHoverCodeBlock(code, language);\n  }\n  return inlineHoverCodeBlock(code, language);\n};\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/htmlCodeBlock.ts",
    "content": "import { CodeBlockFn } from \"@pretty-ts-errors/formatter\";\n\ninterface Highlighter {\n  codeToHtml(code: string, options: { lang: string }): string;\n}\n\nlet highlighter: Highlighter | null = null;\n\nexport function initHighlighter(h: Highlighter) {\n  highlighter = h;\n}\n\nexport const htmlCodeBlock: CodeBlockFn = (code, language, multiLine) => {\n  if (!language) {\n    return `<code>${code}</code>`;\n  }\n\n  if (!highlighter) {\n    throw new Error(\n      \"htmlCodeBlock not initialized. Call initHighlighter() first.\"\n    );\n  }\n\n  const highlighted = highlighter.codeToHtml(code.trim(), {\n    lang: language,\n  });\n\n  if (multiLine) {\n    return (\n      `<p></p>` +\n      `<div class=\"code-container\">` +\n      `<button class=\"copy-button\" data-copy-content><span class=\"codicon codicon-copy\" title=\"Copy type to clipboard\"><span></button>` +\n      highlighted +\n      `</div>` +\n      `<p></p>`\n    );\n  }\n\n  // Inline: strip <pre> wrapper, keep as inline element\n  const inlineHtml = highlighted\n    .replace(/^<pre[^>]*><code[^>]*>/, \"\")\n    .replace(/<\\/code><\\/pre>$/, \"\");\n  return `<code style=\"background-color:var(--vscode-textCodeBlock-background);padding:4px 8px;border-radius:4px;\">${inlineHtml}</code>`;\n};\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/miniLine.ts",
    "content": "import { spanBreak } from \"./spanBreak\";\n\n/** May be useful for line separations */\nexport const miniLine = spanBreak(/*html*/ `<p></p>`);\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/plainCodeBlock.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\n\n/**\n * Code block without syntax highlighting like.\n * For syntax highlighting, use {@link inlineCodeBlock} or {@link multiLineCodeBlock}\n */\nexport const plainCodeBlock = (content: string) => d /*html*/ `\n  <code>${content}</code>\n`;\n"
  },
  {
    "path": "packages/vscode-formatter/src/components/spanBreak.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\n\n/**\n * Since every thing in the extension hover split into spans,\n * we need to close the previous span before we're opening a new one\n * Note: the line breaks is important here\n */\nexport const spanBreak = (children: string) => d /*html*/ `\n  </span>\n  ${children}\n  <span>\n`;\n"
  },
  {
    "path": "packages/vscode-formatter/src/format/embedSymbolLinks.ts",
    "content": "import { Diagnostic } from \"vscode-languageserver-types\";\nimport { URI } from \"vscode-uri\";\n\nexport function embedSymbolLinks(diagnostic: Diagnostic): Diagnostic {\n  if (\n    !diagnostic?.relatedInformation?.[0]?.message?.includes(\"is declared here\")\n  ) {\n    return diagnostic;\n  }\n  const ref = diagnostic.relatedInformation[0];\n  const symbol = ref?.message.match(/(?<symbol>'[^']*') is declared here./)\n    ?.groups?.[\"symbol\"];\n\n  if (!symbol) {\n    return diagnostic;\n  }\n\n  const args = [URI.parse(ref.location.uri), ref.location.range];\n  const href = URI.parse(\n    `command:prettyTsErrors.revealSelection?${encodeURIComponent(\n      JSON.stringify(args)\n    )}`\n  );\n\n  return {\n    ...diagnostic,\n    message: diagnostic.message.replaceAll(\n      symbol,\n      `${symbol} <a href=\"${href}\" title=\"Go to symbol\"><span class=\"codicon codicon-go-to-file\" ></span></a>&nbsp;`\n    ),\n  };\n}\n"
  },
  {
    "path": "packages/vscode-formatter/src/format/identSentences.ts",
    "content": "import { d } from \"@pretty-ts-errors/utils\";\n\nexport const identSentences = (message: string): string =>\n  message\n    .split(\"\\n\")\n    .map((line) => {\n      let whiteSpacesCount = line.search(/\\S/);\n      if (whiteSpacesCount === -1) {\n        whiteSpacesCount = 0;\n      }\n      if (whiteSpacesCount === 0) {\n        return line;\n      }\n      if (whiteSpacesCount >= 2) {\n        whiteSpacesCount -= 2;\n      }\n\n      return d /*html*/ `\n        </span>\n        <p></p>\n        <span>\n        <table>\n        <tr>\n        <td>\n        ${\"&nbsp;\".repeat(3).repeat(whiteSpacesCount)}\n        <span class=\"codicon codicon-indent\"></span>\n        &nbsp;&nbsp;\n        </td>\n        <td>${line}</td>\n        </tr>\n        </table>\n    `;\n    })\n    .join(\"\");\n"
  },
  {
    "path": "packages/vscode-formatter/src/format/prettifyDiagnosticForHover.ts",
    "content": "import { createErrorMessagePrettifier } from \"@pretty-ts-errors/formatter\";\nimport { Diagnostic } from \"vscode-languageserver-types\";\nimport {\n  divider,\n  showErrorInSidebarLink,\n  pinErrorLink,\n  copyErrorLink,\n  errorCodeExplanationLink,\n} from \"../components/actions\";\nimport { errorTitle } from \"../components/errorTitle\";\nimport { miniLine } from \"../components/miniLine\";\nimport { d } from \"@pretty-ts-errors/utils\";\nimport { embedSymbolLinks } from \"./embedSymbolLinks\";\nimport { identSentences } from \"./identSentences\";\nimport { hoverCodeBlock } from \"../components/hoverCodeBlock\";\n\nconst prettifyErrorMessageForHover =\n  createErrorMessagePrettifier(hoverCodeBlock);\n\n/**\n * Prettify a diagnostic for display in hover tooltips.\n * Uses markdown fenced code blocks (required by VS Code's MarkdownString).\n */\nexport async function prettifyDiagnosticForHover(\n  diagnostic: Diagnostic\n): Promise<string> {\n  const newDiagnostic = embedSymbolLinks(diagnostic);\n  const identedSentences = identSentences(newDiagnostic.message);\n  const prettifiedMessage =\n    await prettifyErrorMessageForHover(identedSentences);\n\n  return d /*html*/ `\n    ${errorTitle(\n      newDiagnostic.code,\n      d`${showErrorInSidebarLink(newDiagnostic.range, diagnostic.message)} ${divider}\n        ${pinErrorLink(newDiagnostic.range, diagnostic.message)} ${divider}\n        ${copyErrorLink(newDiagnostic.message)} ${divider}\n        ${errorCodeExplanationLink(newDiagnostic.code)}`,\n      miniLine\n    )}\n    <span>\n    ${prettifiedMessage}\n    </span>\n  `;\n}\n"
  },
  {
    "path": "packages/vscode-formatter/src/format/prettifyDiagnosticForSidebar.ts",
    "content": "import { createErrorMessagePrettifier } from \"@pretty-ts-errors/formatter\";\nimport { Diagnostic } from \"vscode-languageserver-types\";\nimport { htmlCodeBlock } from \"../components/htmlCodeBlock\";\nimport {\n  divider,\n  pinErrorLink,\n  copyErrorLink,\n  errorMessageTranslationLink,\n  errorCodeExplanationLink,\n} from \"../components/actions\";\nimport { errorTitle } from \"../components/errorTitle\";\nimport { d } from \"@pretty-ts-errors/utils\";\nimport { embedSymbolLinks } from \"./embedSymbolLinks\";\nimport { identSentences } from \"./identSentences\";\n\nconst prettifyErrorMessageForSidebar =\n  createErrorMessagePrettifier(htmlCodeBlock);\n\n/**\n * Prettify a diagnostic for display in the sidebar webview.\n * Uses shiki HTML code blocks (must call initHtmlCodeBlock before first use).\n */\nexport async function prettifyDiagnosticForSidebar(\n  diagnostic: Diagnostic\n): Promise<string> {\n  const newDiagnostic = embedSymbolLinks(diagnostic);\n  const identedSentences = identSentences(newDiagnostic.message);\n  const prettifiedMessage =\n    await prettifyErrorMessageForSidebar(identedSentences);\n\n  return d /*html*/ `\n    ${errorTitle(\n      newDiagnostic.code,\n      d`${pinErrorLink(newDiagnostic.range, diagnostic.message)} ${divider}\n        ${copyErrorLink(newDiagnostic.message)} ${divider}\n        ${errorMessageTranslationLink(newDiagnostic.message)} ${divider}\n        ${errorCodeExplanationLink(newDiagnostic.code)}`\n    )}\n    <div style=\"line-height:2; padding-top: 8px;\">\n    ${prettifiedMessage}\n    </div>\n  `;\n}\n"
  },
  {
    "path": "packages/vscode-formatter/src/index.ts",
    "content": "export { prettifyDiagnosticForHover } from \"./format/prettifyDiagnosticForHover\";\nexport { prettifyDiagnosticForSidebar } from \"./format/prettifyDiagnosticForSidebar\";\nexport { initHighlighter } from \"./components/htmlCodeBlock\";\n"
  },
  {
    "path": "packages/vscode-formatter/test/vscode-formatter.vitest.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { createErrorMessagePrettifier } from \"@pretty-ts-errors/formatter\";\nimport { hoverCodeBlock } from \"../src/components/hoverCodeBlock\";\nimport { plainCodeBlock } from \"../src/components/plainCodeBlock\";\nimport {\n  errorWithSpecialCharsInObjectKeys,\n  errorWithDashInObjectKeys,\n} from \"../../formatter/test/errorMessageMocks\";\n\nconst prettifyErrorMessage = createErrorMessagePrettifier(hoverCodeBlock);\n\ndescribe(\"hoverCodeBlock\", () => {\n  it(\"renders inline code with language\", () => {\n    const result = hoverCodeBlock(\"string\", \"type\", false);\n    expect(result).toContain(\"```type\");\n    expect(result).toContain(\"string\");\n    expect(result).toContain(\"```\");\n  });\n\n  it(\"renders multiline code with padding\", () => {\n    const code = \"{\\n  name: string\\n}\";\n    const result = hoverCodeBlock(code, \"type\", true);\n    expect(result).toContain(\"```type\");\n    expect(result).toContain(\"<p></p>\");\n  });\n\n  it(\"falls back to plainCodeBlock when no language\", () => {\n    const result = hoverCodeBlock(\"someCode\", undefined, false);\n    expect(result).toContain(\"<code>\");\n    expect(result).toContain(\"someCode\");\n  });\n});\n\ndescribe(\"plainCodeBlock\", () => {\n  it(\"wraps content in a code tag\", () => {\n    const result = plainCodeBlock(\"hello\");\n    expect(result).toContain(\"<code>hello</code>\");\n  });\n});\n\ndescribe(\"formatDiagnosticMessage with hoverCodeBlock\", () => {\n  it(\"formats special characters in object keys\", async () => {\n    const result = await prettifyErrorMessage(\n      errorWithSpecialCharsInObjectKeys\n    );\n    expect(result).toContain(\"```type\");\n    expect(result).toContain(\"string\");\n    expect(result).toContain(\"abc*bc\");\n  });\n\n  it(\"formats dash in object keys\", async () => {\n    const result = await prettifyErrorMessage(errorWithDashInObjectKeys);\n    expect(result).toContain(\"```type\");\n    expect(result).toContain(\"first-name\");\n    expect(result).toContain(\"string\");\n  });\n});\n"
  },
  {
    "path": "packages/vscode-formatter/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"references\": [\n    {\n      \"path\": \"../formatter\"\n    },\n    {\n      \"path\": \"../utils\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/vscode-formatter/vitest.config.ts",
    "content": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  test: {\n    environment: \"node\",\n    globals: true,\n    include: [\"test/**/*.vitest.{ts,tsx}\", \"test/**/*.spec.{ts,tsx}\"],\n  },\n});\n"
  },
  {
    "path": "tsconfig.base.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"CommonJS\",\n    \"moduleResolution\": \"Node\",\n    \"lib\": [\"ES2022\", \"ES2021.String\"],\n    \"baseUrl\": \".\",\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"useDefineForClassFields\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"resolveJsonModule\": true,\n    \"sourceMap\": true,\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"moduleDetection\": \"force\",\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"composite\": true\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"extends\": \"./tsconfig.base.json\",\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"packages/utils\" },\n    { \"path\": \"packages/formatter\" },\n    { \"path\": \"packages/vscode-formatter\" },\n    { \"path\": \"apps/vscode-extension\" }\n  ]\n}\n"
  },
  {
    "path": "tsdown.config.mjs",
    "content": "import { defineConfig } from \"tsdown\";\n\nexport default defineConfig({\n  ignoreWatch: [\".turbo/\"],\n});\n"
  },
  {
    "path": "turbo.json",
    "content": "{\n  \"$schema\": \"https://turbo.build/schema.json\",\n  \"globalDependencies\": [\"**/.env.*local\"],\n  \"tasks\": {\n    \"build\": {\n      \"outputs\": [\"dist/**\"]\n    },\n    \"lint\": {},\n    \"dev\": {\n      \"cache\": false,\n      \"persistent\": true\n    }\n  }\n}\n"
  }
]