Socket
Socket
Sign inDemoInstall

shiki-twoslash

Package Overview
Dependencies
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shiki-twoslash - npm Package Compare versions

Comparing version 0.6.1 to 0.6.2

21

dist/index.d.ts
import { Highlighter } from "shiki/dist/highlighter";
import { TwoSlashReturn } from "@typescript/twoslash";
/** Checks if it is available in shiki */
export declare const canHighlightLang: (lang: string) => boolean;
export declare const createShikiHighlighter: (options: import("shiki/dist/highlighter").HighlighterOptions) => Highlighter | Promise<Highlighter>;
import { TwoSlashOptions, TwoSlashReturn } from "@typescript/twoslash";
export declare type ShikiTwoslashSettings = {

@@ -10,6 +7,18 @@ useNodeModules?: true;

};
/** Checks if a particular lang is available in shiki */
export declare const canHighlightLang: (lang: string) => boolean;
/**
* Creates a Shiki highlighter, this is an async call because of the call to WASM to get the regex parser set up.
*
* In other functions, passing a the result of this highlighter function is kind of optional but it's the author's
* opinion that you should be in control of the highlighter, and not this library.
*
*/
export declare const createShikiHighlighter: (options: import("shiki/dist/highlighter").HighlighterOptions) => Highlighter | Promise<Highlighter>;
export declare const defaultShikiTwoslashSettings: ShikiTwoslashSettings;
/** Uses Shiki to render the code to HTML */
export declare const renderCodeToHTML: (code: string, lang: string, highlighter?: Highlighter | undefined, twoslash?: TwoSlashReturn | undefined) => string;
/** Runs Twoslash over the code in the */
export declare const runTwoSlash: (code: string, lang: string, settings?: ShikiTwoslashSettings) => TwoSlashReturn;
/**
* Runs Twoslash over the code passed in with a particular language as the default file.
*/
export declare const runTwoSlash: (code: string, lang: string, settings?: ShikiTwoslashSettings, twoslashDefaults?: TwoSlashOptions) => TwoSlashReturn;

@@ -10,2 +10,20 @@ 'use strict';

function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
var splice = function splice(str, idx, rem, newString) {

@@ -288,3 +306,3 @@ return str.slice(0, idx) + newString + str.slice(idx + Math.abs(rem));

[].concat(shikiLanguages.commonLangIds, shikiLanguages.commonLangAliases, shikiLanguages.otherLangIds);
/** Checks if it is available in shiki */
/** Checks if a particular lang is available in shiki */

@@ -301,2 +319,10 @@ var canHighlightLang = function canHighlightLang(lang) {

var storedHighlighter = null;
/**
* Creates a Shiki highlighter, this is an async call because of the call to WASM to get the regex parser set up.
*
* In other functions, passing a the result of this highlighter function is kind of optional but it's the author's
* opinion that you should be in control of the highlighter, and not this library.
*
*/
var createShikiHighlighter = function createShikiHighlighter(options) {

@@ -340,8 +366,10 @@ if (storedHighlighter) return storedHighlighter;

return results;
}; // Basically so that we can store this once, then re-use it
}; // Basically so that we can store this once, then re-use it in the same process
var nodeModulesMap = undefined;
/** Runs Twoslash over the code in the */
/**
* Runs Twoslash over the code passed in with a particular language as the default file.
*/
var runTwoSlash = function runTwoSlash(code, lang, settings) {
var runTwoSlash = function runTwoSlash(code, lang, settings, twoslashDefaults) {
if (settings === void 0) {

@@ -351,2 +379,6 @@ settings = defaultShikiTwoslashSettings;

if (twoslashDefaults === void 0) {
twoslashDefaults = {};
}
var map = undefined; // Shiki doesn't respect json5 as an input, so switch it

@@ -379,5 +411,5 @@ // to json, which can handle comments in the syntax highlight

var results = twoslash.twoslasher(code, lang, {
var results = twoslash.twoslasher(code, lang, _extends({}, twoslashDefaults, {
fsMap: map
});
}));
return results;

@@ -384,0 +416,0 @@ };

@@ -1,2 +0,2 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("shiki"),n=require("shiki-languages"),t=require("@typescript/twoslash"),r=require("@typescript/vfs");function a(e){var n={"<":"lt",'"':"quot","'":"apos","&":"amp","\r":"#10","\n":"#13"};return e.toString().replace(/[<"'\r\n&]/g,(function(e){return"&"+n[e]+";"}))}function s(e){return e.replace(/</g,"&lt;").replace(/>/g,"&gt;")}function o(e,n){var t=new Map;return e.forEach((function(e){var r=n(e),a=t.get(r);a?a.push(e):t.set(r,[e])})),t}var i=[].concat(n.commonLangIds,n.commonLangAliases,n.otherLangIds),c=null,l={},u=void 0;exports.canHighlightLang=function(e){return i.includes(e)},exports.createShikiHighlighter=function(n){if(c)return c;var t,r=(n||{}).theme||"nord";try{t=e.getTheme(r)}catch(n){try{t=e.loadTheme(r)}catch(e){throw new Error("Unable to load theme: "+r+" - "+e.message)}}return e.getHighlighter({theme:t,langs:i}).then((function(e){return c=e}))},exports.defaultShikiTwoslashSettings=l,exports.renderCodeToHTML=function(e,n,t,r){if(!t&&!c)throw new Error("The highlighter object hasn't been initialised via `setupHighLighter` yet in render-shiki-twoslash");return function(e,n,t){if(!t)return function(e,n){var t="";return t+='<pre class="shiki">',n.langId&&(t+='<div class="language-id">'+n.langId+"</div>"),t+="<div class='code-container'><code>",e.forEach((function(e){0===e.length?t+="\n":(e.forEach((function(e){t+='<span style="color: '+e.color+'">'+s(e.content)+"</span>"})),t+="\n")})),t=t.replace(/\n*$/,""),t+="</code></div></pre>"}(e,n);var r="";r+='<pre class="shiki twoslash">',n.langId&&(r+='<div class="language-id">'+n.langId+"</div>"),r+="<div class='code-container'><code>";var i,c=o(t.errors,(function(e){return e.line}))||new Map,l=o(t.staticQuickInfos,(function(e){return e.line}))||new Map,u=o(t.queries,(function(e){return e.line-1}))||new Map,p=0;return e.forEach((function(e,n){var t=c.get(n)||[],o=l.get(n)||[],i=u.get(n)||[];if(0===e.length&&0===n)p+=1;else if(0===e.length)p+=1,r+="\n";else{var d=0;e.forEach((function(e){var n="",s=function(n){return function(t){return n<=t.character&&n+e.content.length>=t.character+t.length}},c=t.filter(s(d)),l=o.filter(s(d)),u=i.filter(s(d)),h=[].concat(c,l,u).sort((function(e,n){return(e.start||0)-(n.start||0)}));n+=h.length?function(e,n){var t=[],r=!1;e.forEach((function(e){"lsp"===e.classes?(t.push({text:"⇍/data-lsp⇏",index:e.end}),t.push({text:"⇍data-lsp lsp=⇯"+(e.lsp||"")+"⇯⇏",index:e.begin})):"err"===e.classes?r=!0:"query"===e.classes&&(t.push({text:"⇍/data-highlight⇏",index:e.end}),t.push({text:"⇍data-highlight'⇏",index:e.begin}))}));var s=(" "+n).slice(1);return t.sort((function(e,n){return n.index-e.index})).forEach((function(e){var n,t,r;r=e.text,s=(n=s).slice(0,t=e.index)+r+n.slice(t+Math.abs(0))})),r&&(s="⇍data-err⇏"+s+"⇍/data-err⇏"),a(s).replace(/⇍/g,"<").replace(/⇏/g,">").replace(/⇯/g,"'")}(h.map((function(e){var n={begin:e.start-p,end:e.start+e.length-p};return"renderedMessage"in e&&(n.classes="err"),"kind"in e&&(n.classes=e.kind),"targetString"in e&&(n.classes="lsp",n.lsp=a(e.text)),n})),e.content):e.content.replace(/</g,"⇍").replace(/>/g,"⇏").replace(/'/g,"⇯"),r+='<span style="color: '+e.color+'">'+n+"</span>",d+=e.content.length,p+=e.content.length})),r+="\n",p+=1}if(t.length){var h=t.map((function(e){return s(e.renderedMessage)})).join("</br>"),g=t.map((function(e){return e.code})).join("<br/>");r+='<span class="error"><span>'+h+'</span><span class="code">'+g+"</span></span>",r+='<span class="error-behind">'+h+"</span>"}i.length&&(i.forEach((function(e){switch(e.kind){case"query":r+="<span class='query'>//"+"".padStart(e.offset-2)+"^ = "+e.text+"</span>";break;case"completions":if(e.completions){var n=e.completions.filter((function(n){return n.name.startsWith(e.completionsPrefix||"____")}));console.log("Prefix: ",e.completionsPrefix);var t=n.sort((function(e,n){return e.name.localeCompare(n.name)})).map((function(n){var t,r=n.name.substr((null===(t=e.completionsPrefix)||void 0===t?void 0:t.length)||0);return"<li><span><span class='result-found'>"+(e.completionsPrefix||"")+"</span>"+r+"<span></li>"})).join("");r+="".padStart(e.offset)+"<span class='inline-completions'><ul class='dropdown'>"+t+"</ul></span>"}else r+="<span class='query'>//"+"".padStart(e.offset-2)+"^ - No completions found</span>"}})),r+="\n")})),i=r.replace(/\n*$/,""),r=i.replace(/⇍/g,"&lt;").replace(/⇏/g,"&gt;").replace(/⇯/g,"&apos;"),r+="</code><a href='"+t.playgroundURL+"'>Try</a></div></pre>"}((t||c).codeToThemedTokens(e,n),{langId:n},r)},exports.runTwoSlash=function(e,n,a){void 0===a&&(a=l);var s=void 0,o={json5:"json"};return o[n]&&(n=o[n]),a.useNodeModules&&(u?s=new Map(u):(s=r.createDefaultMapFromNodeModules({target:6}),u=s),r.addAllFilesFromFolder(s,a.nodeModulesTypesPath||"node_modules/@types")),t.twoslasher(e,n,{fsMap:s})};
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("shiki"),n=require("shiki-languages"),t=require("@typescript/twoslash"),r=require("@typescript/vfs");function a(){return(a=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e}).apply(this,arguments)}function s(e){var n={"<":"lt",'"':"quot","'":"apos","&":"amp","\r":"#10","\n":"#13"};return e.toString().replace(/[<"'\r\n&]/g,(function(e){return"&"+n[e]+";"}))}function o(e){return e.replace(/</g,"&lt;").replace(/>/g,"&gt;")}function i(e,n){var t=new Map;return e.forEach((function(e){var r=n(e),a=t.get(r);a?a.push(e):t.set(r,[e])})),t}var c=[].concat(n.commonLangIds,n.commonLangAliases,n.otherLangIds),l=null,u={},p=void 0;exports.canHighlightLang=function(e){return c.includes(e)},exports.createShikiHighlighter=function(n){if(l)return l;var t,r=(n||{}).theme||"nord";try{t=e.getTheme(r)}catch(n){try{t=e.loadTheme(r)}catch(e){throw new Error("Unable to load theme: "+r+" - "+e.message)}}return e.getHighlighter({theme:t,langs:c}).then((function(e){return l=e}))},exports.defaultShikiTwoslashSettings=u,exports.renderCodeToHTML=function(e,n,t,r){if(!t&&!l)throw new Error("The highlighter object hasn't been initialised via `setupHighLighter` yet in render-shiki-twoslash");return function(e,n,t){if(!t)return function(e,n){var t="";return t+='<pre class="shiki">',n.langId&&(t+='<div class="language-id">'+n.langId+"</div>"),t+="<div class='code-container'><code>",e.forEach((function(e){0===e.length?t+="\n":(e.forEach((function(e){t+='<span style="color: '+e.color+'">'+o(e.content)+"</span>"})),t+="\n")})),t=t.replace(/\n*$/,""),t+="</code></div></pre>"}(e,n);var r="";r+='<pre class="shiki twoslash">',n.langId&&(r+='<div class="language-id">'+n.langId+"</div>"),r+="<div class='code-container'><code>";var a,c=i(t.errors,(function(e){return e.line}))||new Map,l=i(t.staticQuickInfos,(function(e){return e.line}))||new Map,u=i(t.queries,(function(e){return e.line-1}))||new Map,p=0;return e.forEach((function(e,n){var t=c.get(n)||[],a=l.get(n)||[],i=u.get(n)||[];if(0===e.length&&0===n)p+=1;else if(0===e.length)p+=1,r+="\n";else{var d=0;e.forEach((function(e){var n="",o=function(n){return function(t){return n<=t.character&&n+e.content.length>=t.character+t.length}},c=t.filter(o(d)),l=a.filter(o(d)),u=i.filter(o(d)),h=[].concat(c,l,u).sort((function(e,n){return(e.start||0)-(n.start||0)}));n+=h.length?function(e,n){var t=[],r=!1;e.forEach((function(e){"lsp"===e.classes?(t.push({text:"⇍/data-lsp⇏",index:e.end}),t.push({text:"⇍data-lsp lsp=⇯"+(e.lsp||"")+"⇯⇏",index:e.begin})):"err"===e.classes?r=!0:"query"===e.classes&&(t.push({text:"⇍/data-highlight⇏",index:e.end}),t.push({text:"⇍data-highlight'⇏",index:e.begin}))}));var a=(" "+n).slice(1);return t.sort((function(e,n){return n.index-e.index})).forEach((function(e){var n,t,r;r=e.text,a=(n=a).slice(0,t=e.index)+r+n.slice(t+Math.abs(0))})),r&&(a="⇍data-err⇏"+a+"⇍/data-err⇏"),s(a).replace(/⇍/g,"<").replace(/⇏/g,">").replace(/⇯/g,"'")}(h.map((function(e){var n={begin:e.start-p,end:e.start+e.length-p};return"renderedMessage"in e&&(n.classes="err"),"kind"in e&&(n.classes=e.kind),"targetString"in e&&(n.classes="lsp",n.lsp=s(e.text)),n})),e.content):e.content.replace(/</g,"⇍").replace(/>/g,"⇏").replace(/'/g,"⇯"),r+='<span style="color: '+e.color+'">'+n+"</span>",d+=e.content.length,p+=e.content.length})),r+="\n",p+=1}if(t.length){var h=t.map((function(e){return o(e.renderedMessage)})).join("</br>"),g=t.map((function(e){return e.code})).join("<br/>");r+='<span class="error"><span>'+h+'</span><span class="code">'+g+"</span></span>",r+='<span class="error-behind">'+h+"</span>"}i.length&&(i.forEach((function(e){switch(e.kind){case"query":r+="<span class='query'>//"+"".padStart(e.offset-2)+"^ = "+e.text+"</span>";break;case"completions":if(e.completions){var n=e.completions.filter((function(n){return n.name.startsWith(e.completionsPrefix||"____")}));console.log("Prefix: ",e.completionsPrefix);var t=n.sort((function(e,n){return e.name.localeCompare(n.name)})).map((function(n){var t,r=n.name.substr((null===(t=e.completionsPrefix)||void 0===t?void 0:t.length)||0);return"<li><span><span class='result-found'>"+(e.completionsPrefix||"")+"</span>"+r+"<span></li>"})).join("");r+="".padStart(e.offset)+"<span class='inline-completions'><ul class='dropdown'>"+t+"</ul></span>"}else r+="<span class='query'>//"+"".padStart(e.offset-2)+"^ - No completions found</span>"}})),r+="\n")})),a=r.replace(/\n*$/,""),r=a.replace(/⇍/g,"&lt;").replace(/⇏/g,"&gt;").replace(/⇯/g,"&apos;"),r+="</code><a href='"+t.playgroundURL+"'>Try</a></div></pre>"}((t||l).codeToThemedTokens(e,n),{langId:n},r)},exports.runTwoSlash=function(e,n,s,o){void 0===s&&(s=u),void 0===o&&(o={});var i=void 0,c={json5:"json"};return c[n]&&(n=c[n]),s.useNodeModules&&(p?i=new Map(p):(i=r.createDefaultMapFromNodeModules({target:6}),p=i),r.addAllFilesFromFolder(i,s.nodeModulesTypesPath||"node_modules/@types")),t.twoslasher(e,n,a({},o,{fsMap:i}))};
//# sourceMappingURL=shiki-twoslash.cjs.production.min.js.map

@@ -6,2 +6,20 @@ import { getTheme, loadTheme, getHighlighter } from 'shiki';

function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
var splice = function splice(str, idx, rem, newString) {

@@ -284,3 +302,3 @@ return str.slice(0, idx) + newString + str.slice(idx + Math.abs(rem));

[].concat(commonLangIds, commonLangAliases, otherLangIds);
/** Checks if it is available in shiki */
/** Checks if a particular lang is available in shiki */

@@ -297,2 +315,10 @@ var canHighlightLang = function canHighlightLang(lang) {

var storedHighlighter = null;
/**
* Creates a Shiki highlighter, this is an async call because of the call to WASM to get the regex parser set up.
*
* In other functions, passing a the result of this highlighter function is kind of optional but it's the author's
* opinion that you should be in control of the highlighter, and not this library.
*
*/
var createShikiHighlighter = function createShikiHighlighter(options) {

@@ -336,8 +362,10 @@ if (storedHighlighter) return storedHighlighter;

return results;
}; // Basically so that we can store this once, then re-use it
}; // Basically so that we can store this once, then re-use it in the same process
var nodeModulesMap = undefined;
/** Runs Twoslash over the code in the */
/**
* Runs Twoslash over the code passed in with a particular language as the default file.
*/
var runTwoSlash = function runTwoSlash(code, lang, settings) {
var runTwoSlash = function runTwoSlash(code, lang, settings, twoslashDefaults) {
if (settings === void 0) {

@@ -347,2 +375,6 @@ settings = defaultShikiTwoslashSettings;

if (twoslashDefaults === void 0) {
twoslashDefaults = {};
}
var map = undefined; // Shiki doesn't respect json5 as an input, so switch it

@@ -375,5 +407,5 @@ // to json, which can handle comments in the syntax highlight

var results = twoslasher(code, lang, {
var results = twoslasher(code, lang, _extends({}, twoslashDefaults, {
fsMap: map
});
}));
return results;

@@ -380,0 +412,0 @@ };

{
"name": "shiki-twoslash",
"version": "0.6.1",
"version": "0.6.2",
"license": "MIT",
"homepage": "https://github.com/microsoft/TypeScript-Website/",
"description": "API primitives to mix Shiki with Twoslash",
"author": "Orta Therox",

@@ -7,0 +8,0 @@ "main": "./dist/index.js",

### shiki-twoslash
Sets up markdown code blocks to run through [shiki](https://shiki.matsu.io) which means it gets the VS Code quality
syntax highlighting. This part is basically the same as [gatsby-remark-shiki](https://www.gatsbyjs.org/packages/gatsby-remark-shiki/).
Provides the API primitives to mix [shiki](https://shiki.matsu.io) with [@typescript/twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher).
Why Shiki? Shiki uses the same syntax highlighter engine as VS Code, which means no matter how complex your code is - it will syntax highlight correctly.
Things it handles:
In addition to all the languages shiki handles ([it's a lot](https://github.com/octref/shiki/blob/master/packages/languages/README.md#literal-values)), this module adds opt-in [@typescript/twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher) rendering for TypeScript code blocks.
- Shiki bootstrapping: `createShikiHighlighter`
- Checking if shiki can handle a code sample: `canHighlightLang`
- Running Twoslash over code, with caching and DTS lookups: `runTwoSlash`
- Rendering any code sample with Shiki: `renderCodeToHTML`
This module powers the code samples on the TypeScript website.
### API
![](https://user-images.githubusercontent.com/49038/78996047-ca7be880-7b11-11ea-9e6e-fa7ea8854993.png)
The user-exposed parts of the API is a single file, you might find it easier to just read that: [`src/index.ts`](https://github.com/microsoft/TypeScript-website/blob/v2/packages/shiki-twoslash/src/index.ts).
With a bit of work you can explain complicated code in a way that lets people introspect at their own pace.
##### `createShikiHighlighter`
## Plugin Setup
Sets up the highlighter for Shiki, accepts shiki options:
#### Express Setup
```ts
async function visitor(highlighterOpts, shikiOpts) {
const highlighter = await createShikiHighlighter(highlighterOpts)
visit(markdownAST, "code", visitor(highlighter, shikiOpts))
}
```
[Read this PR](https://github.com/orta/gatsby-twoslash-shiki-blog/pull/1) and apply the same to your project.
##### `renderCodeToHTML`
#### Setup
Renders source code into HTML via Shiki:
1. **Install the dependency**: `yarn add shiki-twoslash`
1. **Include `"shiki-twoslash"` in the plugins section** of `gatsby-transformer-remark`
```ts
const shouldHighlight = lang && canHighlightLang(lang)
```diff
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
"gatsby-remark-autolink-headers",
+ {
+ resolve: "shiki-twoslash",
+ options: {
+ theme: "nord",
+ }
+ },
"gatsby-remark-copy-linked-files",
"gatsby-remark-smartypants",
],
},
}
```
if (shouldHighlight) {
const results = renderCodeToHTML(node.value, lang, highlighter)
node.type = "html"
node.children = []
}
```
If you have `gatsby-remark-prismjs` in, delete it from the config and run `yarn remove gatsby-remark-prismjs`.
To get access to the twoslash renderer, you'll need to pass in the results of a twoslash run to `renderCodeToHTML`:
1. **Add the CSS**
```ts
const highlighter = await createShikiHighlighter(highlighterOpts)
const twoslashResults = runTwoSlash(code, lang)
const html = renderCodeToHTML(twoslashResults.code, twoslashResults.lang, highlighter)
```
This CSS comes from the [TypeScript website's scss](https://github.com/microsoft/TypeScript-website/blob/v2/packages/typescriptlang-org/src/templates/markdown-twoslash.scss)
#### `runTwoSlash`
You should consider it a base to work from, rather than a perfect for every project reference.
Used to run Twoslash on a code sample. In this case it's looking at a code AST node and switching out the HTML with the twoslash results:
```css
/* Code blocks look like:
<pre class='shiki twoslash'>
<div class='language-id>[lang-id]</div>
<div class='code-container'>
<code>[the code as a series of spans]</code>
</div>
</pre>
*/
pre {
/* In theory shiki will overwrite these, but this is to make sure there are defaults */
background-color: white;
color: black;
/* Give it some space to breathe */
padding: 12px;
/* All code samples get a grey border, twoslash ones get a different color */
border-left: 1px solid #999;
border-bottom: 1px solid #999;
margin-bottom: 3rem;
/* Important to allow the code to move horizontally; */
overflow: auto;
position: relative;
}
pre.shiki {
overflow: initial;
}
/* So that folks know you can highlight */
pre.twoslash {
border-color: #719af4;
}
/* The code inside should scroll, but the overflow can't be on the shiki because it would not allow the relative positioning */
pre .code-container {
overflow: auto;
}
/* Handle scrolling, and showing code correctly */
pre code {
white-space: pre;
-webkit-overflow-scrolling: touch;
}
/* Let errors use the outer shiki for their absolute sizing, and not be affected by the scrolling of the code */
pre data-err {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
repeat-x bottom left;
padding-bottom: 3px;
}
/* In order to have the 'popped out' style design and to not break the layout
/* we need to place a fake and un-selectable copy of the error which _isn't_ broken out
/* behind the actual error message.
/* This section keeps both of those in sync */
pre .error,
pre .error-behind {
margin-left: -20px;
margin-top: 8px;
margin-bottom: 4px;
padding: 6px;
padding-left: 14px;
white-space: pre-wrap;
display: block;
}
pre .error {
position: absolute;
background-color: #ffeeee;
border-left: 2px solid #bf1818;
width: calc(100% - 14px);
/* Give the space to the error code */
display: flex;
align-items: center;
color: black;
}
pre .error .code {
display: none;
}
pre .error-behind {
user-select: none;
color: #ffeeee;
}
data-lsp {
/* Ensures there's no 1px jump when the hover happens above */
border-bottom: 1px dotted transparent;
/* Fades in unobtrusively */
transition-timing-function: ease;
transition: border-color 0.3s;
}
/* Respect people's wishes to not have animations */
@media (prefers-reduced-motion: reduce) {
data-lsp {
transition: none;
}
}
/** When you mouse over the pre, show the underlines */
pre:hover data-lsp {
border-color: #747474;
}
/** The tooltip-like which provides the LSP response */
#twoslash-mouse-hover-info {
background-color: #3f3f3f;
color: #fff;
text-align: left;
padding: 5px 8px;
border-radius: 2px;
font-family: "JetBrains Mono", Menlo, Monaco, Consolas, Courier New, monospace;
font-size: 14px;
white-space: pre-wrap;
}
```
1. **Add the JS** for hover info to your component:
```jsx
import React, { useEffect } from "react"
import { setupTwoslashHovers } from "shiki-twoslash/dom";
export default () => {
// Add a the hovers
useEffect(setupTwoslashHovers, [])
// Normal JSX for your component
return </>
}
```
### Verify
With that set up, start up your server and add a codeblock to a markdown file to see if it renders with highlights:
<pre>```json
{ "json": true }
```</pre>
If that works, then add a twoslash example:
<pre>```ts twoslash
interface IdLabel {id: number, /* some fields */ }
interface NameLabel {name: string, /* other fields */ }
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel;
// This comment should not be included
// ---cut---
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
throw "unimplemented"
}
let a = createLabel("typescript");`
```</pre>
If the code sample shows as
```ts
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
throw "unimplemented"
if (node.meta && node.meta.includes("twoslash")) {
const results = runTwoSlash(node.value, node.lang, settings)
node.value = results.code
node.lang = results.extension
node.twoslash = results
}
let a = createLabel("typescript")
```
Then it worked, and you should be able to hover over `createLabel` to see it's types.
### Used in:
### Plugin Config
This plugin passes the config options directly to Shiki. You probably will want to
[set `theme`](https://github.com/octref/shiki/blob/master/packages/themes/README.md#shiki-themes).
- [gatsby-remark-shiki-twoslash](https://www.npmjs.com/package/gatsby-remark-shiki-twoslash)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc