Skip to content

Commit

Permalink
Dev playground with monaco-editor (#1)
Browse files Browse the repository at this point in the history
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
gdamjan authored Jan 11, 2024
1 parent 01151d2 commit 612d1c8
Show file tree
Hide file tree
Showing 10 changed files with 741 additions and 17 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/playground.yml
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules/
dist/
playground/types/
playground/dist/
.tsbuildinfo
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
# `irclog-api`
# `@irclogs/api`

Typescript API to access the IrcLog CouchDB database. Mostly typed wrappers over the http api of CouchDB.


## Quick start for users

```
TBD
echo "@irclogs:registry=https://npm.pkg.github.com" >> .npmrc
npm install @irclogs/api
# or
yarn add @irclogs/api
# or
pnpm add @irclogs/api
```

## Quick start for developers
## API Playground

TBD
See https://irclogs.github.io/api/ to interactively play with the api in your browser.

To use the playground locally run:
```
pnpm install
pnpm dev
pnpm playground:types
pnpm play
```
and open http://localhost:8000


## References

Expand All @@ -27,6 +37,5 @@ pnpm dev

- naming: class name?
- documentation
- playground publish to gh-pages (https://www.typescriptlang.org/dev/sandbox/)
- package publish check auto-versioning schema
- validation on responses and typing (use zod)
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@
"/dist"
],
"scripts": {
"dist": "tsc",
"dist": "rm -rf dist/ && tsc",
"compile": "tsc --noEmit --pretty",
"prettier": "prettier src/",
"prettier:fix": "prettier src/ --write",
"play": "esbuild src/index.ts --bundle --outdir=playground/dist --watch --servedir=playground --format=esm",
"playground:dist": "esbuild src/index.ts --bundle --outdir=playground/dist --format=esm",
"playground:types": "$_ dist && dts-bundle --name @irclogs/api --baseDir ./dist/ --main dist/index.d.ts --out $PWD/playground/dist/index.d.ts",
"test": "echo Warning: no test specified for now!"
},
"devDependencies": {
"prettier": "^3.1.0",
"typescript": "^5.3.2"
"dts-bundle": "^0.7.3",
"esbuild": "^0.19.10",
"prettier": "^3.1.1",
"typescript": "^5.3.3"
},
"prettier": {
"printWidth": 120,
Expand Down
70 changes: 70 additions & 0 deletions playground/README.md
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`.
8 changes: 8 additions & 0 deletions playground/example.ts
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);
Binary file added playground/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions playground/index.html
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>
Loading

0 comments on commit 612d1c8

Please sign in to comment.