├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── README.md ├── README.zh-CN.md ├── images ├── 31db67de83c2de209755814f2d892326846df0e1d03cf94a42e69228b1a181a0.gif ├── 7d2e61ffca9179651ae071db60671f1c8c3500fb7d4d254f4322873d75ed422c.png └── e729298e337f291f715efdcb9fd5a7999d6563beb4fb27d9e845c681f75b3fe2.png ├── package.json ├── rollup.config.js ├── scripts └── getDTS.ts ├── src ├── components │ ├── App.svelte │ ├── Challenges.svelte │ ├── Description.svelte │ └── TypeHelper.svelte ├── dom.ts ├── fetch.ts ├── index.ts ├── logger.ts ├── markdown.ts ├── stores.ts ├── typeHelpers.ts ├── types.ts └── vendor │ ├── ds │ └── createDesignSystem.d.ts │ ├── playground.d.ts │ ├── pluginUtils.d.ts │ ├── sandbox.d.ts │ ├── tsWorker.d.ts │ ├── typescript-vfs.d.ts │ └── utils.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | dist 71 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .gitignore 3 | rollup.config.jss 4 | !dist 5 | scripts 6 | .vscode 7 | yarn* 8 | tsconfig.json 9 | rollup* 10 | -------------------------------------------------------------------------------- /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/`. 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/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 | ## Adding a dependency 31 | 32 | Because most node_modules expect to be running in node, you might have to do some work to get the dependency working on the web. 33 | 34 | The route to handle this is via rollup: 35 | 36 | - add a new dependency via `yarn add xyz` 37 | - import it into your `index.ts` 38 | - run `yarn build` - did it provide some error messages? 39 | - If it did, you may need to edit your `rollup.config.js`. 40 | - You could probably start by taking the [rollup config from `playground-plugin-tsquery`](https://github.com/orta/playground-plugin-tsquery/blob/master/rollup.config.js) and by adding any extra externals and globals. 41 | 42 | #### Serve 43 | 44 | [Serve](https://github.com/zeit/serve) is used to make a web-server for the dist folder. 45 | 46 | ## Deployment 47 | 48 | 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.) 49 | 50 | For example, this is how you can handle getting the URL for a CSS file which is included in your `dist` folder: 51 | 52 | ```ts 53 | const isDev = document.location.host.includes("localhost") 54 | const unpkgURL = "https://unpkg.com/typescript-playground-presentation-mode@latest/dist/slideshow.css" 55 | const cssHref = isDev ? "http://localhost:5000/slideshow.css" : unpkgURL 56 | ``` 57 | 58 | ### Post-Deploy 59 | 60 | 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. 61 | 62 | Once you're happy and it's polished, you can apply to have it in the default plugin list. 63 | 64 | ## Support 65 | 66 | 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. 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## @type-challenges/playground-plugin 2 | 3 | [简体中文](./README.zh-CN.md) 4 | 5 | TypeScript Playground Plugin for [Type Challenges](https://github.com/type-challenges/type-challenges). 6 | 7 | ![picture 1](images/31db67de83c2de209755814f2d892326846df0e1d03cf94a42e69228b1a181a0.gif) 8 | 9 | Supported Features: 10 | 11 | - [x] Pick question in challenge list 12 | - [x] Switch languages 13 | - [x] Copy and share answer to GitHub issue 14 | - [x] Quick start next question 15 | - [x] Quick find solutions 16 | 17 | ## Getting Started 18 | 19 | ### Step 1 20 | 21 | [Open TypeScript Playground with Plugin](https://www.typescriptlang.org/play?install-plugin=%40type-challenges%2Fplayground-plugin) 22 | 23 | ### Step 2 24 | 25 | ![picture 2](images/e729298e337f291f715efdcb9fd5a7999d6563beb4fb27d9e845c681f75b3fe2.png) 26 | 27 | Confirm "OK". 28 | 29 | ### Step 3 30 | 31 | ![picture 3](images/7d2e61ffca9179651ae071db60671f1c8c3500fb7d4d254f4322873d75ed422c.png) 32 | 33 | Start the challenge in TypeScript Playground! 34 | 35 | ## Contributing 36 | 37 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full details, however, TLDR: 38 | 39 | ```sh 40 | git clone ... 41 | yarn 42 | yarn start 43 | ``` 44 | 45 | tick the **Connect to localhost:5000** in bottom of Plugins Panel, and refresh browser page. 46 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | ## @type-challenges/playground-plugin 2 | 3 | [English](./README.md) 4 | 5 | 在 TypeScript Playground 中轻松练习 [Type Challenges](https://github.com/type-challenges/type-challenges)。 6 | 7 | ![picture 1](images/31db67de83c2de209755814f2d892326846df0e1d03cf94a42e69228b1a181a0.gif) 8 | 9 | 功能支持: 10 | 11 | - [x] 从题目列表中选择,直接进入答题模式 12 | - [x] 语言切换 13 | - [x] 复制答案跳转至 GitHub issue 进行分析 14 | - [x] 下一题快捷按钮 15 | - [x] 快速定位解答列表 16 | 17 | ## 起步 18 | 19 | ### 步骤一 20 | 21 | [点击链接,打开 TypeScript Playground](https://www.typescriptlang.org/play?install-plugin=%40type-challenges%2Fplayground-plugin) 22 | 23 | ### 步骤二 24 | 25 | ![picture 2](images/e729298e337f291f715efdcb9fd5a7999d6563beb4fb27d9e845c681f75b3fe2.png) 26 | 27 | 确认安装插件。 28 | 29 | ### Step 3 30 | 31 | ![picture 3](images/7d2e61ffca9179651ae071db60671f1c8c3500fb7d4d254f4322873d75ed422c.png) 32 | 33 | 开始挑战吧! 34 | 35 | ## 如何贡献代码 36 | 37 | 完整信息请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md),简单描述: 38 | 39 | ```sh 40 | git clone ... 41 | yarn 42 | yarn start 43 | ``` 44 | 45 | 勾选 Plugin 面板底部的 **Connect to localhost:5000** 选项,并刷新页面。 46 | -------------------------------------------------------------------------------- /images/31db67de83c2de209755814f2d892326846df0e1d03cf94a42e69228b1a181a0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/type-challenges/playground-plugin/46de85704e6789b063e1e48fd8a3a074a4a1a744/images/31db67de83c2de209755814f2d892326846df0e1d03cf94a42e69228b1a181a0.gif -------------------------------------------------------------------------------- /images/7d2e61ffca9179651ae071db60671f1c8c3500fb7d4d254f4322873d75ed422c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/type-challenges/playground-plugin/46de85704e6789b063e1e48fd8a3a074a4a1a744/images/7d2e61ffca9179651ae071db60671f1c8c3500fb7d4d254f4322873d75ed422c.png -------------------------------------------------------------------------------- /images/e729298e337f291f715efdcb9fd5a7999d6563beb4fb27d9e845c681f75b3fe2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/type-challenges/playground-plugin/46de85704e6789b063e1e48fd8a3a074a4a1a744/images/e729298e337f291f715efdcb9fd5a7999d6563beb4fb27d9e845c681f75b3fe2.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@type-challenges/playground-plugin", 3 | "displayName": "Type Challenges Integration", 4 | "version": "0.1.0", 5 | "main": "dist/index.js", 6 | "description": "Playground integration for Type Challenges", 7 | "license": "MIT", 8 | "keywords": [ 9 | "playground-plugin", 10 | "type-challenges" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/type-challenges/playground-type-challenges-plugin" 15 | }, 16 | "scripts": { 17 | "test": "svelte-check", 18 | "build": "rollup -c rollup.config.js", 19 | "compile": "tsc", 20 | "bootstrap": "ts-node scripts/getDTS.ts", 21 | "dev": "yarn start", 22 | "start": "concurrently -p \"[{name}]\" -n \"ROLLUP,SITE\" -c \"bgBlue.bold,bgMagenta.bold\" \"yarn rollup -c rollup.config.js --watch\" \"yarn serve dist -p 5000\"", 23 | "prepublishOnly": "yarn build", 24 | "postinstall": "yarn bootstrap && yarn build", 25 | "release": "npx bumpp --commit --tag --push && yarn publish --non-interactive" 26 | }, 27 | "devDependencies": { 28 | "@rollup/plugin-commonjs": "^11.0.2", 29 | "@rollup/plugin-json": "^4.0.2", 30 | "@rollup/plugin-node-resolve": "^7.1.0", 31 | "@rollup/plugin-typescript": "^3.0.0", 32 | "@types/markdown-it": "^10.0.2", 33 | "@types/node": "^14.14.9", 34 | "@types/node-fetch": "^2.5.7", 35 | "@types/prismjs": "^1.16.2", 36 | "@types/react": "^16.9.23", 37 | "concurrently": "^5.1.0", 38 | "markdown-it": "^12.0.2", 39 | "monaco-editor": "^0.19.3", 40 | "node-fetch": "^2.6.0", 41 | "rollup": "^1.31.0", 42 | "rollup-plugin-svelte": "^6.1.1", 43 | "serve": "^13.0.2", 44 | "svelte-check": "^1.1.13", 45 | "svelte-preprocess": "^4.6.0", 46 | "ts-node": "^9.0.0", 47 | "typescript": "^4.5.2" 48 | }, 49 | "dependencies": { 50 | "svelte": "^3.29.7", 51 | "tslib": "^1.10.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript' 2 | import node from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import json from '@rollup/plugin-json' 5 | import svelte from 'rollup-plugin-svelte' 6 | import sveltePreprocess from 'svelte-preprocess' 7 | 8 | // You can have more root bundles by extending this array 9 | const rootFiles = ['index.ts'] 10 | 11 | export default rootFiles.map(name => { 12 | /** @type { import("rollup").RollupOptions } */ 13 | const options = { 14 | input: `src/${name}`, 15 | external: ['typescript'], 16 | output: { 17 | name, 18 | dir: 'dist', 19 | format: 'amd', 20 | }, 21 | plugins: [ 22 | svelte({ preprocess: sveltePreprocess({}) }), 23 | typescript({ tsconfig: 'tsconfig.json' }), 24 | commonjs(), 25 | node(), 26 | json(), 27 | ], 28 | } 29 | 30 | return options 31 | }) 32 | 33 | /** Note: 34 | * if you end up wanting to import a dependency which relies on typescript, you will need 35 | * settings which adds these extra options. It will re-use the window.ts for the typescript 36 | * dependency, and I've not figured a way to remove fs and path. 37 | * 38 | const options = { 39 | external: ['typescript', 'fs', 'path'], 40 | output: { 41 | paths: { 42 | "typescript":"typescript-sandbox/index", 43 | "fs":"typescript-sandbox/index", 44 | "path":"typescript-sandbox/index", 45 | }, 46 | }, 47 | plugins: [typescript({ tsconfig: "tsconfig.json" }), externalGlobals({ typescript: "window.ts" }), commonjs(), node(), json()] 48 | }; 49 | * 50 | */ 51 | -------------------------------------------------------------------------------- /scripts/getDTS.ts: -------------------------------------------------------------------------------- 1 | // Grab the DTS files from the TypeScript website 2 | // then do a bit of string manipulation in order to make it 3 | // compile without _all_ of the dependencies 4 | 5 | import { promises as fs, constants as fsConsts } from 'fs' 6 | import * as path from 'path' 7 | import fetch from 'node-fetch' 8 | 9 | function download(url: string): Promise { 10 | return fetch(url).then((r) => r.text()) 11 | } 12 | 13 | function saveFile(dest: string, content: string): Promise { 14 | return fs.writeFile(path.join(__dirname, '..', dest), content, 'utf8') 15 | } 16 | 17 | async function isNotExists(path: string): Promise { 18 | try { 19 | await fs.access(path, fsConsts.F_OK) 20 | return false 21 | } catch { 22 | return true 23 | } 24 | } 25 | 26 | ;(async () => { 27 | const vendor = path.join('src', 'vendor') 28 | const ds = path.join(vendor, 'ds') 29 | 30 | if (await isNotExists(ds)) { 31 | await fs.mkdir(ds, { recursive: true }) 32 | } 33 | 34 | const host = 'https://www.staging-typescript.org' 35 | // For playground-dev purposes 36 | // const host = 'http://localhost:8000' 37 | 38 | /** The API for the monaco typescript worker. */ 39 | async function getTSWorker() { 40 | const text = await download(`${host}/js/sandbox/tsWorker.d.ts`) 41 | return saveFile(path.join(vendor, 'tsWorker.d.ts'), text) 42 | } 43 | 44 | async function getDesignSystemDts() { 45 | const text = await download( 46 | `${host}/js/playground/ds/createDesignSystem.d.ts` 47 | ) 48 | return saveFile( 49 | path.join(ds, 'createDesignSystem.d.ts'), 50 | text.replace('typescriptlang-org/static/js/sandbox', '../sandbox') 51 | ) 52 | } 53 | 54 | async function getPluginUtils() { 55 | const text = await download(`${host}/js/playground/pluginUtils.d.ts`) 56 | return saveFile( 57 | path.join(vendor, 'pluginUtils.d.ts'), 58 | text.replace('from "typescript-sandbox"', 'from "./sandbox"') 59 | ) 60 | } 61 | 62 | async function getTSVfs() { 63 | const text = await download(`${host}/js/sandbox/vendor/typescript-vfs.d.ts`) 64 | return saveFile( 65 | path.join(vendor, 'typescript-vfs.d.ts'), 66 | text 67 | .replace('/// ', '') // remove import 68 | .replace('import("lz-string").LZStringStatic', 'any') // remove 'lz-string' 69 | ) 70 | } 71 | 72 | async function getSandbox() { 73 | const text = await download(`${host}/js/sandbox/index.d.ts`) 74 | return saveFile( 75 | path.join(vendor, 'sandbox.d.ts'), 76 | text 77 | .replace('./vendor/typescript-vfs', './typescript-vfs') // replace TS-VFS 78 | .replace('import lzstring', '// import lzstring') // remove 'lz-string' 79 | .replace('lzstring: typeof lzstring', '// lzstring: typeof lzstring') // remove 'lz-string' 80 | ) 81 | } 82 | 83 | async function getPlayground() { 84 | const text = await download(`${host}/js/playground/index.d.ts`) 85 | return saveFile( 86 | path.join(vendor, '/playground.d.ts'), 87 | text 88 | .replace(/typescript-sandbox/g, './sandbox') // replace sandbox 89 | .replace( 90 | /typescriptlang-org\/static\/js\/sandbox\/vendor\/typescript-vfs/g, 91 | './typescript-vfs' 92 | ) // replace TS-VFS 93 | .replace('lzstring: typeof', '// lzstring: typeof') // remove 'lz-string' 94 | .replace('getWorkerProcess', '// getWorkerProcess') // remove worker 95 | .replace('ui:', '// ui:') // remove UI 96 | ) 97 | } 98 | 99 | return Promise.all([ 100 | getTSWorker(), 101 | getDesignSystemDts(), 102 | getPluginUtils(), 103 | getTSVfs(), 104 | getSandbox(), 105 | getPlayground(), 106 | ]) 107 | })() 108 | -------------------------------------------------------------------------------- /src/components/App.svelte: -------------------------------------------------------------------------------- 1 | 67 | 68 |
69 | 81 |
82 | 83 |
84 | 89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 |
98 | 99 | 186 | -------------------------------------------------------------------------------- /src/components/Challenges.svelte: -------------------------------------------------------------------------------- 1 | 75 | 76 |
77 | {@html content} 78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /src/components/Description.svelte: -------------------------------------------------------------------------------- 1 | 134 | 135 |