Easily create TypeScript Playground Plugins with React.
🚧 This project is experimental. If you have any ideas on how to improve this library, any contributions are welcomed. Also, working on TypeScript Playground plugins currently only can work in Chromium based browsers.
Prefer Svelte? Check out https://github.com/gojutin/typescript-playground-plugin-svelte.
- Features
- About
- Getting Started
- usePlugin Hook
- Styling Your Plugin
- More about TypeScript Playground Plugins
- Todos
✅ Write your TypeScript Playground plugin in React and TypeScript.
✅ Interact with the Playground using a strongly-typed React hook.
✅ Create styles with stylesheets or CSS-in-JS with Goober.
✅ Linting with ESLint
The TypeScript Playground V3 beta comes packed with lots of new features, including the ability to create plugins. Per the TypeScript docs:
The new TypeScript Playground allows people to hook into the Playground and extend it in ways in which the TypeScript team don't expect.
The sidebar of the Playground uses the same plugin infrastructure as external plugins, so you have the same level of access as the playground to build interesting projects.
Playground plugins have no fancy frameworks, you're free to inject them at runtime and use them if you need to - but the current plugins are built with the DOM APIs and TypeScript.
This package allows you to use React as a replacement (or addition to) the DOM APIs to create a rich, interactive UI for your plugin.
The TypeScript Playground Plugin API provides a factory function with lifecycle methods that are used to interact with the playground. This library works by mounting a React app inside of the didMount
method that the API exposes. The modelChanged
and modelChangedDebounce
API methods are called any time the code in the editor changes. Custom events are used to broadcast the changes to a context that wraps the app component.
Option 1: Clone the repo
git clone git@github.com:gojutin/typescript-playground-plugin-react.git
Option 2: Click the "Use this template" button in GitHub (Link)
cd typescript-playground-plugin-react && yarn
yarn start
This will start a development server in watch mode, serve the dist
directory at localhost:5000
, and automatically open the TypeScript Playground in Chrome. As you edit any files in the src
directory, the app will recompile and update dist/index.js
, which is the file that is served to the TypeScript Playground.
Note: This does not reload the browser when your files change. In order to see your changes, the browser will need to be manually reloaded each time you make changes to the plugin.
Visit https://www.typescriptlang.org/play.
Select the Options tab and tick the box for Connect to localhost:5000/index.js.
Now, refresh the browser. When the playground reloads, a new tab with your plugin should appear! 🎉
You can make customizations to your plugin by modifying the customPlugin
object in src/index.tsx
. For instance, you can change the displayName
property to change the tab label text for your plugin. See the PlaygroundPlugin
interface in src/plugin/vendor/playground.d.ts
for all of the available options.
This hooks provides all of the method and properties provided by the Plugin API. It accepts a optional config object and returns an object with these properties:
string
The current code in the Monaco editor saved as React state. This value updates on change to the Monaco editor with optional debouncing. Uses sandbox.getText()
(code: string, options: {format: boolean}) => void
Set the code in the Monaco editor with optional formatting. Uses sandbox.setText()
.
() => void
Format the code in the Monaco editor. Alias for sandbox.editor.getAction("editor.action.formatDocument").run()
.
(IMarker & {key: string})[]
Alias for sandbox.monaco.editor.getModelMarkers({})
with added unique key
property. Kept in sync via sandbox.editor.onDidChangeModelDecorations
.
Here is the type definition for IMarker
:
interface IMarker {
owner: string;
resource: Uri;
severity: MarkerSeverity;
code?:
| string
| {
value: string;
link: Uri;
};
message: string;
source?: string;
startLineNumber: number;
startColumn: number;
endLineNumber: number;
endColumn: number;
relatedInformation?: IRelatedInformation[];
tags?: MarkerTag[];
}
(debounce: boolean) => void
Optionally debounce the modelChange
event from the Plugin API. Per the Plugin docs, this is run on a delay and may not fire on every keystroke. The code
property will be updated accordingly.
object
A DOM library for interacting with TypeScript and JavaScript code, which powers the heart of the TypeScript playground. This object provides several properties and methods to interact with the playground. See all of the available types in src/plugin/vendor/sandbox.d.ts
and read more about the sandbox at http://www.typescriptlang.org/v2/dev/sandbox/.
object
The model is an object which Monaco uses to keep track of text in the editor. You can find the full type definition at node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts
.
{ref: HTMLDivElement, width: number, height: number}
This is an object that contains the container div
element that wraps the entire sidebar as well as it's dimensions in reactive React state. You can wrap the dimension values in a useEffect
to be notified of any changes to the size of the container. The React app is mounted to this element. Any style changes to this element will affect the entire sidebar.
(code: string, subtitle?: string, links?: string[]) => void
From window.playground.ui
- This function accepts three arguments (code, subtitle, and links) and opens a model with the values you provide.
(message: string) => void
From window.playground.ui
- This function accepts one argument (message) and and flashes a quick message in the center of the screen.
{
el: (str: string, el: string, container: Element) => void;,
requireURL: (path: string) => string;,
createASTTree: (node: Node) => HTMLDivElement;
}
An object that contains three additional config options and functionality. el
, requireURL
, and createASTTree
. See src/plugin/vendor/pluginUtils.d.ts
for more information.
const {
code,
setCode,
formatCode,
markers,
setDebounce,
sandbox,
model,
container,
flashInfo,
showModal,
utils
} = usePlugin();
// Here are some examples of things you can do:
setDebounce(true);
// Set the code in the Monaco editor
useEffect(() => {
const defaultCode = `const greet = (): string => "Hi👋";`;
setCode(defaultCode, { format: true });
}, []);
// Listen for changes to the code in the Monaco editor
useEffect(() => {
flashInfo("The code was updated.");
showModal(code, "Here is your code");
}, [code]);
const renderMarkers = markers.map(marker => {
return <div key={marker.key}>{marker.message}</div>;
});
// See App.tsx for additional usage examples
This library works with normal CSS stylesheets out of the box by inlining them using PostCSS. It also includes Goober, a super light-weight (<1KB) CSS-in-JS library. There are examples of both styling approaches in src/App.tsx
. Any other styling solutions may require additional rollup configuration.
The container
provided by the usePlayground
hook can also be styled. Be careful as this will affect all tabs in the sidebar.
Official Playground Plugin Documentation
You can create a plugin (without React) from the official plugin template:
npm init typescript-playground-plugin playground-my-plugin
For convenience, this repo contains the CONTRIBUTING.md
file included in the official plugin template. This document contains useful information about how to work with the plugins.
The src/plugin/vendor
directory contains all of the TypeScript type definitions for the TypeScript Playground Plugin API. This is the best place to find the various config options, properties, and methods that are available.
Orta created a really cool plugin that lets you create presentations in the TypeScript playground using Reveal.js. You can check it out here:
https://github.com/orta/playground-slides
He also offered these plugin ideas in this issue.
- An LSP-ish Playground where you can make see the response to specific calls
- An English explainer which explains a complex TS type
- Convert TS dts -> Flow interfaces(flowgen)
- Run tutorials in the playground against live code as a learning tool
- AST Viewer
- ts-query runner
- codemod runner
- Highlight TS vs JS (or type vs value) parts of some code code
- Show all used types in a file
- Show dts files in the current workspace
- Edit an ambient dts file
In no particular order.
- Add ability to import SVGs with
import
syntax without TypeScript errors. - Reload the TypeScript Playground browser tab on plugin changes if possible.
- Add configuration for other styling solutions (Styled Component, Emotion, Styled JSX)
- Add additional examples, including AST Tree.
- Add ability to destructure named imports
import React, { useEffect } from "react";
if possible. - Incorporate Jest and React Testing Library.