Skip to content

Commit 10b5c1f

Browse files
authored
feat(twoslash-remote): new package with HTTP client and /server subexport (#87)
1 parent 7089e57 commit 10b5c1f

21 files changed

Lines changed: 1937 additions & 16 deletions

docs/components.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
/* eslint-disable */
22
// @ts-nocheck
3+
// biome-ignore lint: disable
4+
// oxlint-disable
5+
// ------
36
// Generated by unplugin-vue-components
47
// Read more: https://github.com/vuejs/core/pull/3399
8+
59
export {}
610

711
/* prettier-ignore */

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@vueuse/core": "catalog:",
1313
"floating-vue": "catalog:",
1414
"fuse.js": "catalog:",
15+
"oxc-minify": "catalog:",
1516
"sass": "catalog:",
1617
"unocss": "catalog:",
1718
"vitepress": "catalog:",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"twoslash": "workspace:*",
4949
"twoslash-cdn": "workspace:*",
5050
"twoslash-eslint": "workspace:*",
51+
"twoslash-remote": "workspace:*",
5152
"twoslash-vue": "workspace:*",
5253
"typescript": "catalog:",
5354
"unbuild": "catalog:",

packages/twoslash-remote/LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2024-PRESENT Anthony Fu <https://github.com/antfu>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6+
associated documentation files (the "Software"), to deal in the Software without restriction,
7+
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9+
subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all copies or substantial
12+
portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15+
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/twoslash-remote/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# twoslash-remote
2+
3+
[![npm version][npm-version-src]][npm-version-href]
4+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
5+
6+
Run [Twoslash](https://github.com/twoslashes/twoslash) by delegating resolution to a remote HTTP API. Keep the heavy dependencies (TypeScript, type acquisition, virtual file system, Vue language server, ...) on a server and ship only a tiny client to the browser, build pipeline, or edge function.
7+
8+
The package exposes two entry points:
9+
10+
- `twoslash-remote` — a client that sends a code snippet + metadata to your endpoint and resolves the [`TwoslashGenericResult`](https://github.com/twoslashes/twoslash/blob/main/packages/twoslash-protocol/src/types/returns.ts) you get back.
11+
- `twoslash-remote/server` — web-standard `Request → Response` helpers to implement the contract on the server side, with any backend that returns a `TwoslashGenericResult` (`twoslash`, `twoslash-eslint`, `twoslash-vue`, or your own).
12+
13+
## Usage
14+
15+
### Client
16+
17+
```ts
18+
import { createTwoslashFromRemote } from 'twoslash-remote'
19+
20+
const twoslash = createTwoslashFromRemote({
21+
endpoint: 'https://my-api.example.com/twoslash',
22+
})
23+
24+
const result = await twoslash.run(
25+
`
26+
const count = 1
27+
// ^?
28+
`,
29+
'ts',
30+
// Optional: serializable twoslasher options forwarded to the backend
31+
{ compilerOptions: { strict: true } },
32+
// Optional: transport-level options (meta, headers, signal)
33+
{ meta: { tenant: 'docs-site' } },
34+
)
35+
36+
console.log(result) // { code: '...', nodes: [...] }
37+
```
38+
39+
The third argument is forwarded to the server backend as `options` in the request body. The fourth argument is transport-only (`meta`, `headers`, `signal`).
40+
41+
### Server
42+
43+
`createTwoslashRequestHandler` returns a web-standard `(req: Request) => Promise<Response>`. It runs in Node ≥ 18, Bun, Deno, Cloudflare Workers, Vercel/Netlify edge functions, and any framework that speaks the fetch API.
44+
45+
```ts
46+
import { createTwoslasher } from 'twoslash'
47+
import { createTwoslashRequestHandler } from 'twoslash-remote/server'
48+
49+
const twoslasher = createTwoslasher()
50+
51+
export default createTwoslashRequestHandler({
52+
twoslasher,
53+
cors: true,
54+
})
55+
```
56+
57+
Plug in any backend that returns a `TwoslashGenericResult``twoslash-eslint`, `twoslash-vue`, or a custom wrapper:
58+
59+
```ts
60+
import { createTwoslasher as createESLintTwoslasher } from 'twoslash-eslint'
61+
import { createTwoslashRequestHandler } from 'twoslash-remote/server'
62+
63+
const twoslasher = createESLintTwoslasher({ eslintConfig: [/* ... */] })
64+
65+
export default createTwoslashRequestHandler({ twoslasher })
66+
```
67+
68+
Lower-level building blocks (`parseTwoslashRequest`, `serializeTwoslashError`) are also exported for users who want to integrate into a framework manually.
69+
70+
## License
71+
72+
[MIT](./LICENSE) License © 2024-PRESENT [Anthony Fu](https://github.com/antfu)
73+
74+
<!-- Badges -->
75+
76+
[npm-version-src]: https://img.shields.io/npm/v/twoslash-remote?style=flat&colorA=161514&colorB=EAB836
77+
[npm-version-href]: https://npmjs.com/package/twoslash-remote
78+
[npm-downloads-src]: https://img.shields.io/npm/dm/twoslash-remote?style=flat&colorA=161514&colorB=E66041
79+
[npm-downloads-href]: https://npmjs.com/package/twoslash-remote
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineBuildConfig } from 'unbuild'
2+
3+
export default defineBuildConfig({
4+
entries: [
5+
'src/index',
6+
'src/server',
7+
],
8+
declaration: true,
9+
clean: true,
10+
rollup: {
11+
emitCJS: true,
12+
},
13+
})
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"name": "twoslash-remote",
3+
"type": "module",
4+
"version": "0.3.8",
5+
"description": "Run Twoslash by delegating resolution to a remote HTTP API",
6+
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
7+
"license": "MIT",
8+
"funding": "https://github.com/sponsors/antfu",
9+
"homepage": "https://github.com/twoslashes/twoslash#readme",
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/twoslashes/twoslash.git",
13+
"directory": "packages/twoslash-remote"
14+
},
15+
"bugs": "https://github.com/twoslashes/twoslash/issues",
16+
"keywords": [
17+
"twoslash",
18+
"remote",
19+
"http",
20+
"fetch"
21+
],
22+
"sideEffects": false,
23+
"exports": {
24+
".": {
25+
"types": "./dist/index.d.ts",
26+
"import": "./dist/index.mjs",
27+
"require": "./dist/index.cjs"
28+
},
29+
"./server": {
30+
"types": "./dist/server.d.ts",
31+
"import": "./dist/server.mjs",
32+
"require": "./dist/server.cjs"
33+
}
34+
},
35+
"main": "./dist/index.mjs",
36+
"module": "./dist/index.mjs",
37+
"types": "./dist/index.d.ts",
38+
"typesVersions": {
39+
"*": {
40+
"server": [
41+
"./dist/server.d.ts"
42+
],
43+
"*": [
44+
"./dist/*",
45+
"./dist/index.d.ts"
46+
]
47+
}
48+
},
49+
"files": [
50+
"dist"
51+
],
52+
"scripts": {
53+
"build": "unbuild",
54+
"dev": "unbuild --stub",
55+
"prepublishOnly": "nr build"
56+
},
57+
"dependencies": {
58+
"twoslash-protocol": "workspace:*"
59+
}
60+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist
3+
dist-ssr
4+
*.local
5+
.cache
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>twoslash-remote · Shiki + dev server + twoslash-cdn worker</title>
7+
<style>
8+
:root {
9+
color-scheme: dark;
10+
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
11+
}
12+
body {
13+
max-width: 64rem;
14+
margin: 0 auto;
15+
padding: 2em;
16+
background: #0f0f0f;
17+
color: #ddd;
18+
line-height: 1.5;
19+
}
20+
h1 {
21+
margin: 0 0 0.25em;
22+
}
23+
.intro,
24+
.meta {
25+
opacity: 0.75;
26+
font-size: 0.92em;
27+
}
28+
.pipeline {
29+
display: flex;
30+
flex-wrap: wrap;
31+
gap: 0.5em;
32+
margin: 1em 0 2em;
33+
font-size: 0.85em;
34+
opacity: 0.9;
35+
}
36+
.pipeline span {
37+
background: #1a1a1a;
38+
border: 1px solid #2a2a2a;
39+
padding: 0.35em 0.6em;
40+
border-radius: 4px;
41+
}
42+
.pipeline .arrow {
43+
background: transparent;
44+
border: 0;
45+
opacity: 0.6;
46+
}
47+
section {
48+
margin: 1.5em 0;
49+
}
50+
section h3 {
51+
margin: 0 0 0.5em;
52+
font-size: 1em;
53+
opacity: 0.85;
54+
}
55+
pre.shiki {
56+
border-radius: 6px;
57+
padding: 1em;
58+
overflow-x: auto;
59+
}
60+
pre.twoslash {
61+
--twoslash-popup-bg: #222;
62+
}
63+
code {
64+
background: #1f1f1f;
65+
padding: 0.1em 0.3em;
66+
border-radius: 3px;
67+
font-size: 0.92em;
68+
}
69+
.error {
70+
margin: 1em 0;
71+
padding: 1em;
72+
background: #2a1010;
73+
border: 1px solid #5a1010;
74+
border-radius: 6px;
75+
color: #f88;
76+
white-space: pre-wrap;
77+
font-family: ui-monospace, SFMono-Regular, monospace;
78+
font-size: 0.85em;
79+
}
80+
</style>
81+
</head>
82+
<body>
83+
<h1>twoslash-remote demo</h1>
84+
<p class="intro">
85+
Shiki renders in the browser. Twoslash analysis runs on the Vite dev
86+
server's <code>/twoslash</code> endpoint, which delegates to a Node
87+
worker thread running <code>twoslash-cdn</code> with a persistent
88+
filesystem-backed CDN cache.
89+
</p>
90+
<div class="pipeline" aria-hidden="true">
91+
<span>Browser · Shiki + twoslash-remote</span>
92+
<span class="arrow"></span>
93+
<span><code>POST /twoslash</code></span>
94+
<span class="arrow"></span>
95+
<span>Vite plugin middleware</span>
96+
<span class="arrow"></span>
97+
<span>Node worker_threads</span>
98+
<span class="arrow"></span>
99+
<span>twoslash-cdn + ATA</span>
100+
</div>
101+
<div id="app">Loading…</div>
102+
<script type="module" src="./main.ts"></script>
103+
</body>
104+
</html>

0 commit comments

Comments
 (0)