TypeScript Sandbox
A DOM library for interacting with TypeScript and JavaScript code, which powers the heart of the TypeScript playground
You can use the TypeScript sandbox for:
- Building IDE-like experiences for people to explore your library's API
- Building interactive web tools which use TypeScript, with a lot of the Playgrounds developer experience for free
For example, the sandbox to the side has grabbed the Types for DangerJS with no modifications for this code sample. This is because the Playground's Automatic Type Acquisition is enabled by default. It will also look for the same parameters for code, and selection indexes inside the URL.
Try clicking this URL to see that in action.
This library builds on top of the Monaco Editor, providing a higher level API but offering access to all the lower-level APIs via a single sandbox
object.
You can find the code for the TypeScript Sandbox inside the microsoft/TypeScript-Website mono-repo.
Usage
A sandbox uses the same tools as monaco-editor, meaning this library is shipped as an AMD bundle which you can use the VSCode Loader to require
.
Because we need it for the TypeScript website, you can use our hosted copy here.
Get Started
Create a new file: index.html
and paste this code into that file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<div id="loader">Loading...</div>
<div id="monaco-editor-embed" style="height: 800px;" />
<script>
// First set up the VSCode loader in a script tag
const getLoaderScript = document.createElement('script')
getLoaderScript.src = 'https://www.typescriptlang.org/js/vs.loader.js'
getLoaderScript.async = true
getLoaderScript.onload = () => {
// Now the loader is ready, tell require where it can get the version of monaco, and the sandbox
// This version uses the latest version of the sandbox, which is used on the TypeScript website
// For the monaco version you can use unpkg or the TypeSCript web infra CDN
// You can see the available releases for TypeScript here:
// https://typescript.azureedge.net/indexes/releases.json
//
require.config({
paths: {
vs: 'https://typescript.azureedge.net/cdn/4.0.5/monaco/min/vs',
// vs: 'https://unpkg.com/@typescript-deploys/monaco-editor@4.0.5/min/vs',
sandbox: 'https://www.typescriptlang.org/js/sandbox',
},
// This is something you need for monaco to work
ignoreDuplicateModules: ['vs/editor/editor.main'],
})
// Grab a copy of monaco, TypeScript and the sandbox
require(['vs/editor/editor.main', 'vs/language/typescript/tsWorker', 'sandbox/index'], (
main,
_tsWorker,
sandboxFactory
) => {
const initialCode = `import {markdown, danger} from "danger"
export default async function () {
// Check for new @types in devDependencies
const packageJSONDiff = await danger.git.JSONDiffForFile("package.json")
const newDeps = packageJSONDiff.devDependencies.added
const newTypesDeps = newDeps?.filter(d => d.includes("@types")) ?? []
if (newTypesDeps.length){
markdown("Added new types packages " + newTypesDeps.join(", "))
}
}
`
const isOK = main && window.ts && sandboxFactory
if (isOK) {
document.getElementById('loader').parentNode.removeChild(document.getElementById('loader'))
} else {
console.error('Could not get all the dependencies of sandbox set up!')
console.error('main', !!main, 'ts', !!window.ts, 'sandbox', !!sandbox)
return
}
// Create a sandbox and embed it into the the div #monaco-editor-embed
const sandboxConfig = {
text: initialCode,
compilerOptions: {},
domID: 'monaco-editor-embed',
}
const sandbox = sandboxFactory.createTypeScriptSandbox(sandboxConfig, main, window.ts)
sandbox.editor.focus()
})
}
document.body.appendChild(getLoaderScript)
</script>
</html>
Opening the file index.html
in a web browser will load up the same sandbox up at the top of the page.
Some examples of the API
Converting the user's TypeScript into JavaScript
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
// Async because it needs to go
const js = await sandbox.getRunnableJS()
console.log(js)
Get the DTS for the user's editor
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
const dts = await sandbox.getDTSForCode()
console.log(dts)
Make a request for an LSP response
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
// A worker here is a web-worker, set up by monaco-typescript
// which does the computation in the background
const worker = await sandbox.getWorkerProcess()
const definitions = await client.getDefinitionAtPosition(model.uri.toString(), 6)
Change compiler flags using a few different APIs
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
// Hook in to all changes to the compiler
sandbox.setDidUpdateCompilerSettings((newOptions) => {
console.log("Compiler settings changed: ", newOptions)
})
// Update via key value
sandbox.updateCompilerSetting("allowJs", true)
// Update via an object
sandbox.updateCompilerSettings({ jsx: 0 })
// Replace the compiler settings
sandbox.setCompilerSettings({})
Highlight some code in the editor
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
const start = {
lineNumber: 0,
column: 0
}
const end = {
lineNumber: 0,
column: 4
}
const decorations = sandbox.editor.deltaDecorations([], [
{
range: new sandbox.monaco.Range(start.lineNumber, start.column, end.lineNumber, end.column),
options: { inlineClassName: 'error-highlight' },
},
])
Create your own playground.
const sandbox = createTypeScriptSandbox(sandboxConfig, main, ts)
// Use a script to make a JSON file like:
// {
// "file:///node_modules/types/keyboard/index.d.ts": "export const enterKey: string"
// }
//
// Where the keys are the paths, and the values are the source-code. The sandbox
// will use the node resolution lookup strategy by default.
const dtsFiles = {}
Object.keys(dtsFiles).forEach(path => {
sandbox.languageServiceDefaults.addExtraLib(dts[path], path);
});
The API is mainly a light shim over the monaco-editor API with the monaco-typescript API.