-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Dev playground with monaco-editor (#1)
The playground should work both locally (`pnpm play`) and on github pages. Currently published at https://irclogs.github.io/api/. Based on monaco-editor, loads the types of the api and the library, so that you can write typescript and it will run in the browser. The CouchDB database will allow cross-origin access from the `https://irclogs.github.io` and `http://localhost:8000` Origins, so that means you can query the real production database. ps. don't use the snippet here: https://www.typescriptlang.org/dev/sandbox/ gives you millennia old monaco and typescript.
- Loading branch information
Showing
10 changed files
with
741 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
name: Build Playground | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
workflow_dispatch: | ||
|
||
jobs: | ||
playground: | ||
# Allow one concurrent deployment | ||
concurrency: | ||
group: 'pages' | ||
cancel-in-progress: true | ||
|
||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment | ||
permissions: | ||
pages: write # to deploy to Pages | ||
id-token: write # to verify the deployment originates from an appropriate source | ||
|
||
environment: | ||
name: github-pages | ||
url: ${{ steps.deployment.outputs.page_url }} | ||
|
||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 18 | ||
|
||
- uses: pnpm/action-setup@v2 | ||
with: | ||
run_install: true | ||
|
||
- run: pnpm run '/^playground:.*/' | ||
|
||
- name: Upload artifact | ||
uses: actions/upload-pages-artifact@v1 | ||
with: | ||
path: ./playground | ||
|
||
- uses: actions/deploy-pages@v1 | ||
id: deployment |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
node_modules/ | ||
dist/ | ||
playground/types/ | ||
playground/dist/ | ||
.tsbuildinfo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Playground implementation notes | ||
|
||
## monaco-editor | ||
|
||
The playground is based on the [monaco-editor](https://microsoft.github.io/monaco-editor/) and | ||
it runs completely in the browser - no backend is needed. That makes it work on a site hosted | ||
at Github Pages too. | ||
|
||
Monaco-editor is used since it is a decent editor, has built-in support for javascript and typescript, | ||
and runs in the browser. | ||
|
||
The playground code is written in plain javascript and html. | ||
|
||
> [!NOTE] | ||
> Don't be fooled by the playground at https://www.typescriptlang.org/dev/sandbox/. | ||
> Its documentation is incomplete, and installs quite old monaco-editor and typescript. | ||
|
||
## type support in the editor | ||
|
||
In order for monaco-editor to be aware about the types of our project, we need to build a bundled | ||
`.d.ts` files. `tsc` itself doesn't know to properly bundle a `.d.ts` file useful for monaco-editor, | ||
so had to use `dts-bundle` (see `playground:types` in package.json scripts). | ||
|
||
The bundle is created at `playground/dist/index.d.ts`, loaded in the browser with `fetch` and | ||
then loaded in the typescript server with `addExtraLib`, at a virtual path of | ||
`file:///node_modules/@types/@irclogs/api/index.d.ts`. | ||
|
||
Given that the edited content is virtually at `file:///example.ts`, the language server uses the node | ||
module resolution algorithm, by looking for `node_modules`, then `@types/<your library>` etc. | ||
|
||
ps. | ||
there's an option to load each `.d.ts` file separately as created by `tsc`, and add them to the language | ||
server. But that's a bit tedious and the list of files will need to be kept in sync with what `tsc` creates | ||
from the source. | ||
|
||
|
||
## running code in the browser | ||
|
||
I wanted to enable running code in the browser as you would run it in any normal typescript module. That | ||
includes top-level await and support for the `import` statement. For those reasons `eval` is not possible. | ||
|
||
In order to actually run the code, first we call `getEmitOutput` on the editor to get transpiled typescript | ||
to javascript. Then we create a `<script>` dom element, with `type=module`, and set the transpiled code | ||
as it's content. Once the element is added to the DOM, the javascript code will run. | ||
|
||
### import mapping | ||
|
||
[importmap](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) is used to | ||
map the normal `import {} from "@irclogs/api"` usage, to actually load the module from a file via http. | ||
|
||
esbuild was used to create the `esm` bundle in `playground/dist/index.js`, so that it's compatible with | ||
the new-wave javascript module system (`import`). | ||
|
||
|
||
### redirecting console.log | ||
|
||
A preamble is added to the transpiled code to override `console.log` to actually add text into the | ||
`#console-output` html element (a `<pre>` element). Seems hacky, but it works. | ||
|
||
|
||
|
||
## Re-use of the playground | ||
|
||
There's really only 3 places to change `@irclogs/api` in the playground code: | ||
- in `example.ts` of-course | ||
- in the `importmap` in `index.html` | ||
- the virtual path `file:///node_modules/@types/@irclogs/api/index.d.ts` in the playground.js | ||
|
||
and one in `package.json` when building the bundled `index.d.ts` file with dts-bundle `--name @irclogs/api`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import {CouchDB} from "@irclogs/api"; | ||
|
||
type T = "a" | "B"; | ||
let a: T = "a"; | ||
|
||
const db = new CouchDB("https://db.softver.org.mk/irclog/", "_design/log/_view/channel"); | ||
let res = await db.fetchChannelList(); | ||
console.log(res); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Playground</title> | ||
<link rel="icon" type="image/png" href="./favicon.png"> | ||
<script | ||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js" | ||
integrity="sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw==" | ||
crossorigin="anonymous" | ||
referrerpolicy="no-referrer" | ||
></script> | ||
<script src="./playground.js" defer></script> | ||
<script type="importmap"> | ||
{ | ||
"imports": { | ||
"@irclogs/api": "./dist/index.js" | ||
} | ||
} | ||
</script> | ||
|
||
<style> | ||
body { | ||
overflow: hidden; | ||
margin: 0; | ||
} | ||
|
||
#main { | ||
font-size: 12pt; | ||
width: 94%; | ||
padding: 0pt; | ||
margin: 1% 3% 5ex 3%; | ||
} | ||
|
||
#leftPane { | ||
float: left; | ||
height: 90vh; | ||
width: 58%; | ||
margin: 0; | ||
background-color: rgb(255, 221, 227); | ||
|
||
&>#monaco-editor { | ||
height: 100%; | ||
} | ||
} | ||
|
||
#rightPane { | ||
float: right; | ||
height: 90vh; | ||
width: 40%; | ||
margin: 0; | ||
background-color: #eceff3; | ||
display: flex; | ||
flex-direction: column; | ||
|
||
&>pre { | ||
overflow: scroll; | ||
flex-grow: 1; | ||
tab-size: 2; | ||
} | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<div id="main"> | ||
<div id="leftPane"> | ||
<div id="monaco-editor"></div> | ||
</div> | ||
<div id="rightPane"> | ||
<div><button id="run-code">Run Code</button><button id="clear-log">Clear log</button></div> | ||
<pre id="console-output"></pre> | ||
</div> | ||
</div> | ||
</body> | ||
|
||
</html> |
Oops, something went wrong.