├── screenshots ├── screenshot1.png └── screenshot2.png ├── .npmignore ├── tsconfig.json ├── src ├── svelte │ ├── Welcome.svelte │ ├── Logo.svelte │ └── App.svelte ├── types │ └── index.d.ts ├── vendor │ ├── pluginUtils.d.ts │ ├── tsWorker.d.ts │ ├── typescript-vfs.d.ts │ ├── playground.d.ts │ └── sandbox.d.ts └── index.ts ├── .gitignore ├── package.json ├── rollup.config.js ├── scripts └── open-playground.js ├── CONTRIBUTING.md └── README.md /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just214/typescript-playground-plugin-svelte/HEAD/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just214/typescript-playground-plugin-svelte/HEAD/screenshots/screenshot2.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .gitignore 3 | rollup.config.jss 4 | !dist 5 | scripts 6 | .vscode 7 | yarn* 8 | tsconfig.json 9 | rollup* 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "esModuleInterop": true, 5 | "noEmit": true, 6 | "typeRoots": ["src/types", "src/vendor"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/svelte/Welcome.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

TypeScript Playground Plugin

6 |

with Svelte!

7 |
8 | 9 |
10 | 11 | 23 | -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import {ShowModal, FlashInfo} from '../index' 2 | 3 | declare module "*.jpeg"; 4 | declare module "*.jpg"; 5 | declare module "*.png"; 6 | declare module '*.svg' { 7 | const content: any 8 | export default content; 9 | } 10 | 11 | declare global { 12 | interface Window { 13 | playground: { 14 | ui: { 15 | showModal: ShowModal; 16 | flashInfo: FlashInfo; 17 | }; 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/vendor/pluginUtils.d.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "typescript"; 2 | /** Creates a set of util functions which is exposed to Plugins to make it easier to build consistent UIs */ 3 | export declare const createUtils: (sb: any) => { 4 | /** Use this to make a few dumb element generation funcs */ 5 | el: (str: string, el: string, container: Element) => void; 6 | /** Get a relative URL for something in your dist folder depending on if you're in dev mode or not */ 7 | requireURL: (path: string) => string; 8 | /** Returns a div which has an interactive AST a TypeScript AST by passing in the root node */ 9 | createASTTree: (node: Node) => HTMLDivElement; 10 | }; 11 | export declare type PluginUtils = ReturnType; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (http://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # dotenv environment variables file 57 | .env 58 | 59 | # gatsby files 60 | .cache/ 61 | public 62 | 63 | # Mac files 64 | .DS_Store 65 | 66 | # Yarn 67 | yarn-error.log 68 | .pnp/ 69 | .pnp.js 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | dist 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-playground-plugin-svelte", 3 | "description": "Easily create TypeScript Playground Plugins with Svelte.", 4 | "version": "0.0.1", 5 | "main": "dist/index.js", 6 | "keywords": [ 7 | "playground-plugin" 8 | ], 9 | "license": "MIT", 10 | "scripts": { 11 | "start": "npm run watch", 12 | "watch": "cross-env NODE_ENV=development rollup -c -w", 13 | "build": "cross-env NODE_ENV=production rollup -c" 14 | }, 15 | "devDependencies": { 16 | "@rollup/plugin-commonjs": "^11.0.2", 17 | "@rollup/plugin-image": "^2.0.4", 18 | "@rollup/plugin-json": "^4.0.2", 19 | "@rollup/plugin-node-resolve": "^7.1.1", 20 | "@rollup/plugin-replace": "^2.3.1", 21 | "@rollup/plugin-typescript": "^3.0.0", 22 | "chalk": "^3.0.0", 23 | "concurrently": "^5.1.0", 24 | "cross-env": "^7.0.1", 25 | "get-chrome-tabs": "^1.0.0", 26 | "monaco-editor": "^0.20.0", 27 | "node-fetch": "^2.6.0", 28 | "rollup": "^1.32.0", 29 | "rollup-plugin-analyzer": "^3.2.2", 30 | "rollup-plugin-execute": "^1.1.1", 31 | "rollup-plugin-progress": "^1.1.1", 32 | "rollup-plugin-serve": "^1.0.1", 33 | "rollup-plugin-svelte": "^5.1.1", 34 | "rollup-plugin-terser": "^5.2.0", 35 | "serve": "^11.3.0", 36 | "svelte": "^3.19.1", 37 | "typescript": "latest" 38 | }, 39 | "dependencies": { 40 | "tslib": "^1.11.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import replace from "@rollup/plugin-replace"; 3 | import node from "@rollup/plugin-node-resolve"; 4 | import commonjs from "@rollup/plugin-commonjs"; 5 | import json from "@rollup/plugin-json"; 6 | import svelte from "rollup-plugin-svelte"; 7 | import image from "@rollup/plugin-image"; 8 | import execute from "rollup-plugin-execute"; 9 | import progress from "rollup-plugin-progress"; 10 | import { terser } from "rollup-plugin-terser"; 11 | import serve from "rollup-plugin-serve"; 12 | import analyze from "rollup-plugin-analyzer"; 13 | 14 | const isProd = process.env.NODE_ENV === "production"; 15 | 16 | export default { 17 | input: `src/index.ts`, 18 | output: { 19 | name: "index", 20 | dir: "dist", 21 | format: "amd" 22 | }, 23 | plugins: [ 24 | isProd && 25 | analyze({ 26 | summaryOnly: true 27 | }), 28 | progress(), 29 | execute("node scripts/open-playground"), 30 | image(), 31 | typescript({ tsconfig: "tsconfig.json" }), 32 | 33 | svelte({ 34 | // enable run-time checks when not in production 35 | dev: !isProd 36 | }), 37 | replace({ 38 | "process.env.NODE_ENV": JSON.stringify( 39 | isProd ? "production" : "development" 40 | ) 41 | }), 42 | node({ 43 | browser: true, 44 | dedupe: ["svelte"] 45 | }), 46 | commonjs(), 47 | // Minify 48 | isProd && terser(), 49 | json(), 50 | !isProd && 51 | serve({ 52 | contentBase: "dist", 53 | port: 5000 54 | }) 55 | ] 56 | }; 57 | -------------------------------------------------------------------------------- /scripts/open-playground.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script is responsible for opening the TypeScript Playground in Chrome on the first 3 | build when running the "start" script. It also provides some useful console messages. 4 | 5 | It is called by rollup-plugin-execute in rollup.config.js 6 | */ 7 | const exec = require("child_process").exec; 8 | const getChromeTabs = require("get-chrome-tabs"); 9 | const chalk = require("chalk"); 10 | 11 | const PLAYGROUND_URL = "https://www.typescriptlang.org/v2/en/play"; 12 | 13 | function openPlayground() { 14 | exec(`open-cli ${PLAYGROUND_URL} -- 'google chrome'`, function(err) { 15 | if (err) { 16 | console.log( 17 | chalk.red("Error opening the TypeScript Playground. Please try again.") 18 | ); 19 | } else { 20 | const message = chalk.green( 21 | '\n🚀 The TypeScript Playground was opened in Chrome. To view your plugin, select "Options > Connect to localhost:5000/index.js" in the Playground sidebar and refresh the browser tab.' 22 | ); 23 | console.log(message); 24 | } 25 | }); 26 | } 27 | 28 | let tabList = []; 29 | 30 | getChromeTabs() 31 | .then(tabs => { 32 | tabList = tabs; 33 | }) 34 | .catch(err => { 35 | return err; 36 | }) 37 | .finally(() => { 38 | const isPlaygroundOpen = tabList.find(tab => 39 | tab.url.includes(PLAYGROUND_URL) 40 | ); 41 | if (!isPlaygroundOpen) { 42 | openPlayground(); 43 | } else { 44 | console.log( 45 | chalk.green( 46 | "\n🚀 Your plugin has been updated. Please refresh the TypeScript Playground Chrome tab to see your changes." 47 | ) 48 | ); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /src/svelte/Logo.svelte: -------------------------------------------------------------------------------- 1 | 14 | 22 | 28 | 39 | 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to a TypeScript Playground Plugin 2 | 3 | ## Contributing 4 | 5 | You can use `yarn start` to set up both a copy of Rollup to generate the JS, and Serve to host it. 6 | 7 | ```sh 8 | yarn start 9 | ``` 10 | 11 | Then set up the TypeScript playground to connect to a dev plugin at `http://localhost:5000/index.js`. 12 | 13 | #### Plugin API 14 | 15 | The plugin API is documented in the [interface PlaygroundPlugin in `./src/vendor/playground.d.ts`](src/vendor/playground.d.ts) 16 | 17 | Roughly: 18 | 19 | - There are a set of mounting and un-mounting functions which you can use to handle your UI in the sidebar 20 | - There are `modelChanged` methods, which are shortcuts to knowing when the code in monaco editor has changed 21 | 22 | ### Sandbox 23 | 24 | The plugins are passed copies of the TypeScript sandbox, which is a high level API wrapper to the [`monaco-editor`](https://microsoft.github.io/monaco-editor/). You can learn more about the sandbox on [the TypeScript website](http://www.typescriptlang.org/v2/dev/sandbox/ 25 | 26 | #### Rollup 27 | 28 | [Rollup](https://rollupjs.org) is a JavaScript bundler, that will take all of the TypeScript + JavaScript code you reference and then create an AMD bundle for it all. AMD bundles are used in Monaco, TypeScript Sandbox and the Playground - so, this is used for consistency with the rest of the ecosystem. 29 | 30 | #### Serve 31 | 32 | [Serve](https://github.com/zeit/serve) is used to make a web-server for the dist folder. 33 | 34 | ## Deployment 35 | 36 | This module should be deployed to npm when you would like the world to see it, this may mean making your code handle a staging vs production environment (because the URLs will be different.) 37 | 38 | For example, this is how you can handle getting the URL for a CSS file which is included in your `dist` folder: 39 | 40 | ```ts 41 | const isDev = document.location.host.includes('localhost') 42 | const unpkgURL = 'https://unpkg.com/typescript-playground-presentation-mode@latest/dist/slideshow.css' 43 | const cssHref = isDev ? 'http://localhost:5000/slideshow.css' : unpkgURL 44 | ``` 45 | 46 | ### Post-Deploy 47 | 48 | Once this is deployed, you can test it on the TypeScript playground by passing in the name of your plugin on npm to the custom plugin box. This is effectively your staging environment. 49 | 50 | Once you're happy and it's polished, you can apply to have it in the default plugin list. 51 | 52 | ## Support 53 | 54 | Ask questions either on the TypeScript Website issues](https://github.com/microsoft/TypeScript-Website/issues), or in the [TypeScript Community Discord](https://discord.gg/typescript) - in the TypeScript Website channel. 55 | -------------------------------------------------------------------------------- /src/svelte/App.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 |
55 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | {#each $markers as marker} 63 |

Line {marker.startLineNumber}:  64 | {marker.message}

65 | {/each} 66 |
67 | 68 | 121 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import App from "./svelte/App.svelte"; 3 | import { writable, readable, get } from "svelte/store"; 4 | import { PluginUtils } from "./vendor/pluginUtils"; 5 | 6 | export type FlashInfo = (message: string) => void; 7 | 8 | export type ShowModal = { 9 | (code: string, subtitle?: string, links?: string[]): void; 10 | }; 11 | 12 | // }); 13 | // return () => disposable.dispose(); 14 | 15 | // Store Objects 16 | const __sandbox = writable({}); // Internal...for Markers 17 | const markers = readable([], set => { 18 | const sandbox = get(__sandbox); 19 | const disposable = sandbox.editor.onDidChangeModelDecorations(() => { 20 | const allMarkers = sandbox.monaco.editor 21 | .getModelMarkers({}) 22 | .map((marker, index) => { 23 | return { 24 | ...marker, 25 | key: index.toString() 26 | }; 27 | }); 28 | set(allMarkers); 29 | 30 | return () => disposable.dispose(); 31 | }); 32 | }); 33 | const model = writable({}); 34 | const code = writable(""); // Has to be writable for the modelChanged events. 35 | const debounce = writable(true); 36 | 37 | function makePlugin(utils: PluginUtils) { 38 | const customPlugin: import("./vendor/playground").PlaygroundPlugin = { 39 | id: "svelte", 40 | displayName: "Svelte", // The tab label 41 | // willMount: (sandbox, container) => { 42 | // // Not used 43 | // }, 44 | didMount: (sandbox, container) => { 45 | function useDebounce(opt: boolean = true) { 46 | debounce.set(opt); 47 | } 48 | 49 | function formatCode() { 50 | sandbox.editor.getAction("editor.action.formatDocument").run(); 51 | } 52 | 53 | function setCode(userCode, options?: { format: boolean }) { 54 | sandbox.setText(userCode); 55 | if (options) { 56 | options.format && formatCode(); 57 | } 58 | } 59 | 60 | const { flashInfo, showModal } = window.playground.ui; 61 | 62 | const props = { 63 | sandbox, 64 | container, 65 | useDebounce, 66 | setCode, 67 | formatCode, 68 | code, 69 | model, 70 | flashInfo, 71 | showModal, 72 | utils, 73 | markers 74 | }; 75 | 76 | // Mount the app and pass in the store objects as props 77 | new App({ 78 | target: container, 79 | props 80 | }); 81 | }, 82 | modelChanged: (sandbox, _model) => { 83 | if (!get(debounce)) { 84 | code.set(sandbox.getText()); 85 | model.set(_model); 86 | __sandbox.set(sandbox); 87 | } 88 | }, 89 | modelChangedDebounce(sandbox, _model) { 90 | if (get(debounce)) { 91 | code.set(sandbox.getText()); 92 | model.set(_model); 93 | __sandbox.set(sandbox); 94 | } 95 | } 96 | // willUnmount: (sandbox, container) => { 97 | // // Not used 98 | // }, 99 | // didUnmount: (sandbox, container) => { 100 | // // Not used 101 | // } 102 | }; 103 | return customPlugin; 104 | } 105 | 106 | export default makePlugin; 107 | -------------------------------------------------------------------------------- /src/vendor/tsWorker.d.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript'; 2 | export declare class TypeScriptWorker implements ts.LanguageServiceHost { 3 | private _ctx; 4 | private _extraLibs; 5 | private _languageService; 6 | private _compilerOptions; 7 | constructor(ctx: any, createData: any); 8 | getCompilationSettings(): ts.CompilerOptions; 9 | getScriptFileNames(): string[]; 10 | private _getModel; 11 | getScriptVersion(fileName: string): string; 12 | getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined; 13 | getScriptKind?(fileName: string): ts.ScriptKind; 14 | getCurrentDirectory(): string; 15 | getDefaultLibFileName(options: ts.CompilerOptions): string; 16 | isDefaultLibFileName(fileName: string): boolean; 17 | private static clearFiles; 18 | getSyntacticDiagnostics(fileName: string): Promise; 19 | getSemanticDiagnostics(fileName: string): Promise; 20 | getSuggestionDiagnostics(fileName: string): Promise; 21 | getCompilerOptionsDiagnostics(fileName: string): Promise; 22 | getCompletionsAtPosition(fileName: string, position: number): Promise; 23 | getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise; 24 | getSignatureHelpItems(fileName: string, position: number): Promise; 25 | getQuickInfoAtPosition(fileName: string, position: number): Promise; 26 | getOccurrencesAtPosition(fileName: string, position: number): Promise | undefined>; 27 | getDefinitionAtPosition(fileName: string, position: number): Promise | undefined>; 28 | getReferencesAtPosition(fileName: string, position: number): Promise; 29 | getNavigationBarItems(fileName: string): Promise; 30 | getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise; 31 | getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): Promise; 32 | getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise; 33 | findRenameLocations(fileName: string, positon: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise; 34 | getRenameInfo(fileName: string, positon: number, options: ts.RenameInfoOptions): Promise; 35 | getEmitOutput(fileName: string): Promise; 36 | getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: ts.FormatCodeOptions): Promise>; 37 | updateExtraLibs(extraLibs: IExtraLibs): void; 38 | } 39 | export interface IExtraLib { 40 | content: string; 41 | version: number; 42 | } 43 | export interface IExtraLibs { 44 | [path: string]: IExtraLib; 45 | } 46 | -------------------------------------------------------------------------------- /src/vendor/typescript-vfs.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare type System = import('typescript').System; 3 | declare type CompilerOptions = import('typescript').CompilerOptions; 4 | declare type TS = typeof import('typescript'); 5 | export interface VirtualTypeScriptEnvironment { 6 | sys: System; 7 | languageService: import('typescript').LanguageService; 8 | getSourceFile: (fileName: string) => import('typescript').SourceFile | undefined; 9 | createFile: (fileName: string, content: string) => void; 10 | updateFile: (fileName: string, content: string, replaceTextSpan?: import('typescript').TextSpan) => void; 11 | } 12 | /** 13 | * Makes a virtual copy of the TypeScript environment. This is the main API you want to be using with 14 | * typescript-vfs. A lot of the other exposed functions are used by this function to get set up. 15 | * 16 | * @param sys an object which conforms to the TS Sys (a shim over read/write access to the fs) 17 | * @param rootFiles a list of files which are considered inside the project 18 | * @param ts a copy pf the TypeScript module 19 | * @param compilerOptions the options for this compiler run 20 | */ 21 | export declare function createVirtualTypeScriptEnvironment(sys: System, rootFiles: string[], ts: TS, compilerOptions?: CompilerOptions): VirtualTypeScriptEnvironment; 22 | /** 23 | * Grab the list of lib files for a particular target, will return a bit more than necessary (by including 24 | * the dom) but that's OK 25 | * 26 | * @param target The compiler settings target baseline 27 | * @param ts A copy of the TypeScript module 28 | */ 29 | export declare const knownLibFilesForCompilerOptions: (compilerOptions: import("typescript").CompilerOptions, ts: typeof import("typescript")) => string[]; 30 | /** 31 | * Sets up a Map with lib contents by grabbing the necessary files from 32 | * the local copy of typescript via the file system. 33 | */ 34 | export declare const createDefaultMapFromNodeModules: (compilerOptions: import("typescript").CompilerOptions) => Map; 35 | /** 36 | * Create a virtual FS Map with the lib files from a particular TypeScript 37 | * version based on the target, Always includes dom ATM. 38 | * 39 | * @param options The compiler target, which dictates the libs to set up 40 | * @param version the versions of TypeScript which are supported 41 | * @param cache should the values be stored in local storage 42 | * @param ts a copy of the typescript import 43 | * @param lzstring an optional copy of the lz-string import 44 | * @param fetcher an optional replacement for the global fetch function (tests mainly) 45 | * @param storer an optional replacement for the localStorage global (tests mainly) 46 | */ 47 | export declare const createDefaultMapFromCDN: (options: import("typescript").CompilerOptions, version: string, cache: boolean, ts: typeof import("typescript"), lzstring?: any | undefined, fetcher?: typeof fetch | undefined, storer?: Storage | undefined) => Promise>; 48 | /** 49 | * Creates an in-memory System object which can be used in a TypeScript program, this 50 | * is what provides read/write aspects of the virtual fs 51 | */ 52 | export declare function createSystem(files: Map): System; 53 | /** 54 | * Creates an in-memory CompilerHost -which is essentially an extra wrapper to System 55 | * which works with TypeScript objects - returns both a compiler host, and a way to add new SourceFile 56 | * instances to the in-memory file system. 57 | */ 58 | export declare function createVirtualCompilerHost(sys: System, compilerOptions: CompilerOptions, ts: TS): { 59 | compilerHost: import("typescript").CompilerHost; 60 | updateFile: (sourceFile: import("typescript").SourceFile) => boolean; 61 | }; 62 | /** 63 | * Creates an object which can host a language service against the virtual file-system 64 | */ 65 | export declare function createVirtualLanguageServiceHost(sys: System, rootFiles: string[], compilerOptions: CompilerOptions, ts: TS): { 66 | languageServiceHost: import("typescript").LanguageServiceHost; 67 | updateFile: (sourceFile: import("typescript").SourceFile) => void; 68 | }; 69 | export {}; 70 | -------------------------------------------------------------------------------- /src/vendor/playground.d.ts: -------------------------------------------------------------------------------- 1 | // declare type Sandbox = import('./sandbox').Sandbox; 2 | export { PluginUtils } from './pluginUtils'; 3 | export declare type PluginFactory = { 4 | (i: (key: string, components?: any) => string): PlaygroundPlugin; 5 | }; 6 | /** The interface of all sidebar plugins */ 7 | export interface PlaygroundPlugin { 8 | /** Not public facing, but used by the playground to uniquely identify plugins */ 9 | id: string; 10 | /** To show in the tabs */ 11 | displayName: string; 12 | /** Should this plugin be selected when the plugin is first loaded? Let's you check for query vars etc to load a particular plugin */ 13 | shouldBeSelected?: () => boolean; 14 | /** Before we show the tab, use this to set up your HTML - it will all be removed by the playground when someone navigates off the tab */ 15 | willMount?: (sandbox: Sandbox, container: HTMLDivElement) => void; 16 | /** After we show the tab */ 17 | didMount?: (sandbox: Sandbox, container: HTMLDivElement) => void; 18 | /** Model changes while this plugin is actively selected */ 19 | modelChanged?: (sandbox: Sandbox, model: import('monaco-editor').editor.ITextModel) => void; 20 | /** Delayed model changes while this plugin is actively selected, useful when you are working with the TS API because it won't run on every keypress */ 21 | modelChangedDebounce?: (sandbox: Sandbox, model: import('monaco-editor').editor.ITextModel) => void; 22 | /** Before we remove the tab */ 23 | willUnmount?: (sandbox: Sandbox, container: HTMLDivElement) => void; 24 | /** After we remove the tab */ 25 | didUnmount?: (sandbox: Sandbox, container: HTMLDivElement) => void; 26 | /** An object you can use to keep data around in the scope of your plugin object */ 27 | data?: any; 28 | } 29 | interface PlaygroundConfig { 30 | lang: string; 31 | prefix: string; 32 | } 33 | 34 | export type Sandbox = { 35 | config: { 36 | text: string; 37 | useJavaScript: boolean; 38 | compilerOptions: import("monaco-editor").languages.typescript.CompilerOptions; 39 | monacoSettings?: import("monaco-editor").editor.IEditorOptions | undefined; 40 | acquireTypes: boolean; 41 | supportTwoslashCompilerOptions: boolean; 42 | suppressAutomaticallyGettingDefaultText?: true | undefined; 43 | suppressAutomaticallyGettingCompilerFlags?: true | undefined; 44 | logger: { 45 | log: (...args: any[]) => void; 46 | error: (...args: any[]) => void; 47 | }; 48 | domID: string; 49 | }; 50 | supportedVersions: readonly ["2.4.1", "2.7.2", "2.8.1", "3.0.1", "3.1.6", "3.3.3", "3.5.1", "3.6.3", "3.7.5", "3.8.2"]; 51 | editor: import("monaco-editor").editor.IStandaloneCodeEditor; 52 | language: string; 53 | monaco: typeof import("monaco-editor"); 54 | // getWorkerProcess: () => Promise; 55 | tsvfs: typeof import("./typescript-vfs"); 56 | getEmitResult: () => Promise; 57 | getRunnableJS: () => Promise; 58 | getDTSForCode: () => Promise; 59 | getDomNode: () => HTMLElement; 60 | getModel: () => import("monaco-editor").editor.ITextModel; 61 | getText: () => string; 62 | setText: (text: string) => void; 63 | getAST: () => Promise; 64 | ts: typeof import("typescript"); 65 | createTSProgram: () => Promise; 66 | compilerDefaults: import("monaco-editor").languages.typescript.CompilerOptions; 67 | getCompilerOptions: () => import("monaco-editor").languages.typescript.CompilerOptions; 68 | setCompilerSettings: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void; 69 | updateCompilerSetting: (key: string | number, value: any) => void; 70 | updateCompilerSettings: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void; 71 | setDidUpdateCompilerSettings: (func: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void) => void; 72 | // lzstring: typeof import("typescriptlang-org/static/js/sandbox/vendor/lzstring.min"); 73 | getURLQueryWithCompilerOptions: (sandbox: any, paramOverrides?: any) => string; 74 | getTwoSlashComplierOptions: (code: string) => any; 75 | languageServiceDefaults: import("monaco-editor").languages.typescript.LanguageServiceDefaults; 76 | } 77 | 78 | export declare const setupPlayground: (sandbox: Sandbox, monaco: typeof import("monaco-editor"), config: PlaygroundConfig, i: (key: string) => string) => { 79 | exporter: { 80 | openProjectInStackBlitz: () => void; 81 | openProjectInCodeSandbox: () => void; 82 | reportIssue: () => Promise; 83 | copyAsMarkdownIssue: () => Promise; 84 | copyForChat: () => void; 85 | copyForChatWithPreview: () => void; 86 | openInTSAST: () => void; 87 | }; 88 | // ui: import("./createUI").UI; 89 | registerPlugin: (plugin: PlaygroundPlugin) => void; 90 | }; 91 | export declare type Playground = ReturnType; 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typescript-playground-plugin-svelte 2 | 3 | Easily create TypeScript [Playground Plugins](https://www.typescriptlang.org/v2/dev/playground-plugins/) with [Svelte](https://svelte.dev/). 4 | 5 | > 🚧 This project is experimental. If you have any ideas on how to improve this library, any contributions are welcomed. Also, TypeScript Playground plugins currently only work in Chromium based browsers. 6 | 7 | Prefer React? Check out [https://github.com/gojutin/typescript-playground-plugin-react](https://github.com/gojutin/typescript-playground-plugin-react). 8 | 9 | ## Table Of Contents 10 | 11 | 1. [About](#about) 12 | 2. [Getting Started](#getting-started) 13 | 3. [Props](#props) 14 | 4. [Styling Your Plugin](#styling-your-plugin) 15 | 5. [More about TypeScript Playground Plugins](#more-about-typescript-playground-plugins) 16 | 17 | ## About 18 | 19 | The TypeScript Playground V2 comes packed with lots of new features, including the ability to create plugins. Per the TypeScript docs: 20 | 21 | > The new TypeScript Playground allows people to hook into the Playground and extend it in ways in which the TypeScript team don't expect. 22 | > 23 | > 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. 24 | > 25 | > 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. 26 | 27 | Since Svelte can be compiled down to dependency-free JavaScript classes, it offers a great, declarative alternative to writing imperative markup with the DOM APIs. This library allows you to use Svelte as a replacement or addition to the DOM APIs to create a rich, interactive UI for your plugin. 28 | 29 | ## Getting Started 30 | 31 | #### Step 1. Clone this repo and navigate to the directory 32 | 33 | ```sh 34 | git clone git@github.com:gojutin/typescript-playground-plugin-svelte.git 35 | ``` 36 | 37 | ```sh 38 | cd typescript-playground-plugin-svelte 39 | ``` 40 | 41 | #### Step 2. Download dependencies 42 | 43 | ```sh 44 | npm install 45 | ``` 46 | 47 | #### Step 3. Start the development server 48 | 49 | ```sh 50 | npm start 51 | ``` 52 | 53 | This will start a development server in watch mode. 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. 54 | 55 | > _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._ 56 | 57 | #### Step 4. Configure and use your plugin 58 | 59 | You can further customize your plugin by modifying the `customPlugin` object in `src/index.ts`. For instance, you can change the `displayName` property to change the label of the tab for your plugin. See the `PlaygroundPlugin` interface in `vendor/playground.d.ts` for all of the available options. 60 | 61 | Visit [https://www.typescriptlang.org/v2/en/play](https://www.typescriptlang.org/v2/en/play). 62 | 63 | Select the **Options** tab and tick the box for **Connect to localhost:5000/index.js**. 64 | 65 | 66 | 67 | Now, **refresh the browser**. When the playground reeoads, a new tab with your plugin should appear! 🎉 68 | 69 | 70 | 71 | ## Props 72 | 73 | The TypeScript Playground Plugin API provides lifecycle methods that are used to interact with the playground. This library uses a combination of [writable store objects](https://svelte.dev/tutorial/writable-stores) and functions to provide the values and methods provided by these lifecycle methods to the Svelte app via props. The following props are provided to your Svelte app: 74 | 75 | ### **code** 76 | 77 | *Writable store* 78 | 79 | ```typescript 80 | string 81 | ``` 82 | 83 | > Although this is writable store value, you don't want to write to it. See `setCode` and `formatCode` below. 84 | 85 | The current code in the Monaco editor. This value updates on change to the Monaco editor with optional debouncing. Uses `sandbox.getText()`. 86 | 87 | ### **setCode** 88 | 89 | *function* 90 | 91 | ```typescript 92 | (code: string, options: {format: boolean}) => void 93 | ``` 94 | 95 | Set the code in the Monaco editor with optional formatting. Uses `sandbox.setText()`. 96 | 97 | ### **formatCode** 98 | 99 | *function* 100 | 101 | ```typescript 102 | () => void 103 | ``` 104 | 105 | Format the code in the Monaco editor. Alias for `sandbox.editor.getAction("editor.action.formatDocument").run()`. 106 | 107 | ### **markers** 108 | 109 | *Readable store* 110 | 111 | ```typescript 112 | IMarker[] 113 | ``` 114 | 115 | Alias for `sandbox.monaco.editor.getModelMarkers({})`. Kept in sync via `sandbox.editor.onDidChangeModelDecorations`. 116 | 117 | Here is the [type definition](https://github.com/Microsoft/monaco-editor/blob/master/monaco.d.ts#L875) for `IMarker`: 118 | 119 | ```typescript 120 | interface IMarker { 121 | owner: string; 122 | resource: Uri; 123 | severity: MarkerSeverity; 124 | code?: 125 | | string 126 | | { 127 | value: string; 128 | link: Uri; 129 | }; 130 | message: string; 131 | source?: string; 132 | startLineNumber: number; 133 | startColumn: number; 134 | endLineNumber: number; 135 | endColumn: number; 136 | relatedInformation?: IRelatedInformation[]; 137 | tags?: MarkerTag[]; 138 | } 139 | ``` 140 | 141 | ### **setDebounce** 142 | 143 | *function* 144 | 145 | ```typescript 146 | (debounce: boolean) => void 147 | ``` 148 | 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` prop will be updated accordingly. Default is `true`. 149 | 150 | ### **sandbox** 151 | 152 | *object* 153 | 154 | ```typescript 155 | Sandbox 156 | ``` 157 | 158 | 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/](http://www.typescriptlang.org/v2/dev/sandbox/). 159 | 160 | ### **model** 161 | 162 | *Writable store* 163 | 164 | ```typescript 165 | Model 166 | ``` 167 | 168 | 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`. Although this is a writable store, you should not overwrite it. 169 | 170 | ### **container** 171 | 172 | ```typescript 173 | HTMLDivElement 174 | ``` 175 | 176 | The `div` element that wraps the entire sidebar. The Svelte app is mounted to this element. Any style changes to this element will affect the entire sidebar. 177 | 178 | ### **showModal** 179 | 180 | *function* 181 | 182 | ```typescript 183 | (code: string, subtitle?: string, links?: string[]) => void 184 | ``` 185 | From `window.playground.ui` - This function accepts three arguments (code, subtitle, and links) and opens a model with the values you provide. 186 | 187 | ### **flashInfo** 188 | 189 | *function* 190 | 191 | ```typescript 192 | (message: string) => void 193 | ``` 194 | From `window.playground.ui` - This function accepts one argument (message) and and flashes a quick message in the center of the screen. 195 | 196 | ### **utils** 197 | 198 | *object* 199 | 200 | ```typescript 201 | { 202 | el: (str: string, el: string, container: Element) => void;, 203 | requireURL: (path: string) => string;, 204 | createASTTree: (node: Node) => HTMLDivElement; 205 | } 206 | ``` 207 | An object that contains three additional config options and functionality. `el`, `requireURL`, and `createASTTree`. See `src/plugin/vendor/pluginUtils.d.ts` for more information. 208 | 209 |
210 | 211 | You can access them in `App.svelte` like so: 212 | 213 | ```html 214 | 227 | ``` 228 | 229 | 230 | ## Styling your plugin 231 | 232 | Style you Svelte components as normal. All styles defined in your Svelte components are automatically injected into the page at render time. You can read more about styling Svelte components at [https://svelte.dev/docs#style](https://svelte.dev/docs#style). 233 | 234 | You can also apply styles to the `container` element. Be cautious as this will affect all tabs in the sidebar. 235 | 236 | ## More about TypeScript Playground Plugins 237 | 238 | [Official Playground Plugin Documentation](https://www.typescriptlang.org/v2/dev/playground-plugins/) 239 | 240 | You can create a plugin (without Svelte) from the official plugin template: 241 | 242 | ```sh 243 | npm init typescript-playground-plugin playground-my-plugin 244 | ``` 245 | 246 | 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. 247 | 248 | The `src/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. 249 | 250 | ### Need inspiration? 251 | 252 | [Orta](https://github.com/orta) created a really cool plugin that lets you create presentations in the TypeScript playground using Reveal.js. You can check it out here: 253 | 254 | [https://github.com/orta/playground-slides](https://github.com/orta/playground-slides) 255 | 256 | He also offered these plugin ideas in [this](https://github.com/microsoft/TypeScript-Website/issues/221) issue. 257 | 258 | - An LSP-ish Playground where you can make see the response to specific calls 259 | - An English explainer which explains a complex TS type 260 | - Convert TS dts -> Flow interfaces(flowgen) 261 | - Run tutorials in the playground against live code as a learning tool 262 | - AST Viewer 263 | - ts-query runner 264 | - codemod runner 265 | - Highlight TS vs JS (or type vs value) parts of some code code 266 | - Show all used types in a file 267 | - Show dts files in the current workspace 268 | - Edit an ambient dts file -------------------------------------------------------------------------------- /src/vendor/sandbox.d.ts: -------------------------------------------------------------------------------- 1 | import { TypeScriptWorker } from "./tsWorker";// import { TypeScriptWorker } from './tsWorker'; 2 | // import lzstring from './vendor/lzstring.min'; 3 | 4 | import * as tsvfs from './typescript-vfs'; 5 | declare type CompilerOptions = import('monaco-editor').languages.typescript.CompilerOptions; 6 | /** 7 | * These are settings for the playground which are the equivalent to props in React 8 | * any changes to it should require a new setup of the playground 9 | */ 10 | export declare type PlaygroundConfig = { 11 | /** The default source code for the playground */ 12 | text: string; 13 | /** Should it run the ts or js IDE services */ 14 | useJavaScript: boolean; 15 | /** Compiler options which are automatically just forwarded on */ 16 | compilerOptions: CompilerOptions; 17 | /** Optional monaco settings overrides */ 18 | monacoSettings?: import('monaco-editor').editor.IEditorOptions; 19 | /** Acquire types via type acquisition */ 20 | acquireTypes: boolean; 21 | /** Support twoslash compiler options */ 22 | supportTwoslashCompilerOptions: boolean; 23 | /** Get the text via query params and local storage, useful when the editor is the main experience */ 24 | suppressAutomaticallyGettingDefaultText?: true; 25 | /** Suppress setting compiler options from the compiler flags from query params */ 26 | suppressAutomaticallyGettingCompilerFlags?: true; 27 | /** Logging system */ 28 | logger: { 29 | log: (...args: any[]) => void; 30 | error: (...args: any[]) => void; 31 | }; 32 | } & ({ 33 | domID: string; 34 | } | { 35 | elementToAppend: HTMLElement; 36 | }); 37 | /** The default settings which we apply a partial over */ 38 | export declare function defaultPlaygroundSettings(): { 39 | /** The default source code for the playground */ 40 | text: string; 41 | /** Should it run the ts or js IDE services */ 42 | useJavaScript: boolean; 43 | /** Compiler options which are automatically just forwarded on */ 44 | compilerOptions: import("monaco-editor").languages.typescript.CompilerOptions; 45 | /** Optional monaco settings overrides */ 46 | monacoSettings?: import("monaco-editor").editor.IEditorOptions | undefined; 47 | /** Acquire types via type acquisition */ 48 | acquireTypes: boolean; 49 | /** Support twoslash compiler options */ 50 | supportTwoslashCompilerOptions: boolean; 51 | /** Get the text via query params and local storage, useful when the editor is the main experience */ 52 | suppressAutomaticallyGettingDefaultText?: true | undefined; 53 | /** Suppress setting compiler options from the compiler flags from query params */ 54 | suppressAutomaticallyGettingCompilerFlags?: true | undefined; 55 | /** Logging system */ 56 | logger: { 57 | log: (...args: any[]) => void; 58 | error: (...args: any[]) => void; 59 | }; 60 | } & { 61 | domID: string; 62 | }; 63 | /** Creates a sandbox editor, and returns a set of useful functions and the editor */ 64 | export declare const createTypeScriptSandbox: (partialConfig: Partial<{ 65 | /** The default source code for the playground */ 66 | text: string; 67 | /** Should it run the ts or js IDE services */ 68 | useJavaScript: boolean; 69 | /** Compiler options which are automatically just forwarded on */ 70 | compilerOptions: import("monaco-editor").languages.typescript.CompilerOptions; 71 | /** Optional monaco settings overrides */ 72 | monacoSettings?: import("monaco-editor").editor.IEditorOptions | undefined; 73 | /** Acquire types via type acquisition */ 74 | acquireTypes: boolean; 75 | /** Support twoslash compiler options */ 76 | supportTwoslashCompilerOptions: boolean; 77 | /** Get the text via query params and local storage, useful when the editor is the main experience */ 78 | suppressAutomaticallyGettingDefaultText?: true | undefined; 79 | /** Suppress setting compiler options from the compiler flags from query params */ 80 | suppressAutomaticallyGettingCompilerFlags?: true | undefined; 81 | /** Logging system */ 82 | logger: { 83 | log: (...args: any[]) => void; 84 | error: (...args: any[]) => void; 85 | }; 86 | } & { 87 | domID: string; 88 | }> | Partial<{ 89 | /** The default source code for the playground */ 90 | text: string; 91 | /** Should it run the ts or js IDE services */ 92 | useJavaScript: boolean; 93 | /** Compiler options which are automatically just forwarded on */ 94 | compilerOptions: import("monaco-editor").languages.typescript.CompilerOptions; 95 | /** Optional monaco settings overrides */ 96 | monacoSettings?: import("monaco-editor").editor.IEditorOptions | undefined; 97 | /** Acquire types via type acquisition */ 98 | acquireTypes: boolean; 99 | /** Support twoslash compiler options */ 100 | supportTwoslashCompilerOptions: boolean; 101 | /** Get the text via query params and local storage, useful when the editor is the main experience */ 102 | suppressAutomaticallyGettingDefaultText?: true | undefined; 103 | /** Suppress setting compiler options from the compiler flags from query params */ 104 | suppressAutomaticallyGettingCompilerFlags?: true | undefined; 105 | /** Logging system */ 106 | logger: { 107 | log: (...args: any[]) => void; 108 | error: (...args: any[]) => void; 109 | }; 110 | } & { 111 | elementToAppend: HTMLElement; 112 | }>, monaco: typeof import("monaco-editor"), ts: typeof import("typescript")) => { 113 | /** The same config you passed in */ 114 | config: { 115 | text: string; 116 | useJavaScript: boolean; 117 | compilerOptions: import("monaco-editor").languages.typescript.CompilerOptions; 118 | monacoSettings?: import("monaco-editor").editor.IEditorOptions | undefined; 119 | acquireTypes: boolean; 120 | supportTwoslashCompilerOptions: boolean; 121 | suppressAutomaticallyGettingDefaultText?: true | undefined; 122 | suppressAutomaticallyGettingCompilerFlags?: true | undefined; 123 | logger: { 124 | log: (...args: any[]) => void; 125 | error: (...args: any[]) => void; 126 | }; 127 | domID: string; 128 | }; 129 | /** A list of TypeScript versions you can use with the TypeScript sandbox */ 130 | supportedVersions: readonly ["2.4.1", "2.7.2", "2.8.1", "3.0.1", "3.1.6", "3.3.3", "3.5.1", "3.6.3", "3.7.5", "3.8.2"]; 131 | /** The monaco editor instance */ 132 | editor: import("monaco-editor").editor.IStandaloneCodeEditor; 133 | /** Either "typescript" or "javascript" depending on your config */ 134 | language: string; 135 | /** The outer monaco module, the result of require("monaco-editor") */ 136 | monaco: typeof import("monaco-editor"); 137 | /** Gets a monaco-typescript worker, this will give you access to a language server. Note: prefer this for language server work because it happens on a webworker . */ 138 | getWorkerProcess: () => Promise; 139 | /** A copy of require("typescript-vfs") this can be used to quickly set up an in-memory compiler runs for ASTs, or to get complex language server results (anything above has to be serialized when passed)*/ 140 | tsvfs: typeof tsvfs; 141 | /** Get all the different emitted files after TypeScript is run */ 142 | getEmitResult: () => Promise; 143 | /** Gets just the JavaScript for your sandbox, will transpile if in TS only */ 144 | getRunnableJS: () => Promise; 145 | /** Gets the DTS output of the main code in the editor */ 146 | getDTSForCode: () => Promise; 147 | /** The monaco-editor dom node, used for showing/hiding the editor */ 148 | getDomNode: () => HTMLElement; 149 | /** The model is an object which monaco uses to keep track of text in the editor. Use this to directly modify the text in the editor */ 150 | getModel: () => import("monaco-editor").editor.ITextModel; 151 | /** Gets the text of the main model, which is the text in the editor */ 152 | getText: () => string; 153 | /** Shortcut for setting the model's text content which would update the editor */ 154 | setText: (text: string) => void; 155 | /** WIP: Gets the AST of the current text */ 156 | getAST: () => Promise; 157 | /** The module you get from require("typescript") */ 158 | ts: typeof import("typescript"); 159 | /** Create a new Program, a TypeScript data model which represents the entire project. 160 | * 161 | * The first time this is called it has to download all the DTS files which is needed for an exact compiler run. Which 162 | * at max is about 1.5MB - after that subsequent downloads of dts lib files come from localStorage. 163 | * 164 | * You probably want 165 | */ 166 | createTSProgram: () => Promise; 167 | /** The Sandbox's default compiler options */ 168 | compilerDefaults: import("monaco-editor").languages.typescript.CompilerOptions; 169 | /** The Sandbox's current compiler options */ 170 | getCompilerOptions: () => import("monaco-editor").languages.typescript.CompilerOptions; 171 | /** Replace the Sandbox's compiler options */ 172 | setCompilerSettings: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void; 173 | /** Overwrite the Sandbox's compiler options */ 174 | updateCompilerSetting: (key: string | number, value: any) => void; 175 | /** Update a single compiler option in the SAndbox */ 176 | updateCompilerSettings: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void; 177 | /** A way to get callbacks when compiler settings have changed */ 178 | setDidUpdateCompilerSettings: (func: (opts: import("monaco-editor").languages.typescript.CompilerOptions) => void) => void; 179 | /** A copy of lzstring, which is used to archive/unarchive code */ 180 | // lzstring: typeof lzstring; 181 | /** Returns compiler options found in the params of the current page */ 182 | getURLQueryWithCompilerOptions: (sandbox: any, paramOverrides?: any) => string; 183 | /** Returns compiler options in the source code using twoslash notation */ 184 | getTwoSlashComplierOptions: (code: string) => any; 185 | /** Gets to the current monaco-language, this is how you talk to the background webworkers */ 186 | languageServiceDefaults: import("monaco-editor").languages.typescript.LanguageServiceDefaults; 187 | }; 188 | export declare type Sandbox = ReturnType; 189 | export {}; 190 | --------------------------------------------------------------------------------