Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move twoslash-cdn package #3

Merged
merged 6 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
- name: Install
run: nci

- name: Build
run: nr build

- name: Typecheck
run: nr typecheck

Expand Down
104 changes: 103 additions & 1 deletion docs/packages/cdn.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,105 @@
# TwoSlash CDN

// TODO:
Run TwoSlash on the browsers or web workers, with [Auto-Type-Acquisition](https://www.typescriptlang.org/play#example/automatic-type-acquisition) from CDN.

A thin wrapper around `twoslash`, `@typescript/vfs`, `@typescript/ata` to an easy-to-use interface. Huge thanks to the TypeScript team for the heavy-lifting work on [TypeScript Website](https://github.com/microsoft/TypeScript-Website) project.

[CDN Example](https://twoslash-cdn-examples.netlify.app/) | [Example Source File](https://github.com/antfu/twoslashes/blob/main/packages/twoslash-cdn/examples/index.html)

## Usage

```html
<script type="module">
// replace with exact version in production
import { createTwoSlashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'

const twoslash = createTwoSlashFromCDN()

// During `.run()`, it will automatically fetch types from CDN
// for used imports in the code (in this case, `vue` and its dependencies),
// and then resolve the types with TypeScript running on the browser.
const result = await twoslash.run(`
import { ref } from 'vue'
const count = ref(0)
// ^?
`)

console.log(result) // { code: '...', nodes: [...] }
</script>
```

Or to bundle it your own,

```bash
npm i -D twoslash-cdn
```

```ts twoslash
import { createTwoSlashFromCDN } from 'twoslash-cdn'

const twoslash = createTwoSlashFromCDN()
// ...
```

### Cache Persistence

By default, the fetched files are stored in a virtual file system in memory. So that multiple runs can share the same cache. If you want to keep them persistent, you can pass a `storage` option to the factory. The storage supports [unstorage](https://github.com/unjs/unstorage)'s interface, where you can adopt the storage to any supported providers.

```html
<script type="module">
// replace with exact versions in production
import { createTwoSlashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'
import { createStorage } from 'https://esm.sh/unstorage@latest'
import indexedDbDriver from 'https://esm.sh/unstorage@latest/drivers/indexedb'

// An example of using unstorage with IndexedDB to cache the virtual file system
const storage = createStorage({
driver: indexedDbDriver(),
})

const twoslash = createTwoSlashFromCDN({
storage,
})

const result = await twoslash.run(`const foo = 1`)
</script>
```

Refresh the page after loading once, you will see the execution is much faster as the cache is loaded from the local IndexedDB.

### Synchronize Usage

Fetching files from CDN is asynchronous, and there is no way to make the whole process synchronous. But if you can run some asynchronous code beforehand, we do provide API to separate the asynchronous part and the synchronous part.

For example, in [Shikiji](https://shikiji.netlify.app/), the `codeToHtml` function is synchronous as well as the [`shikiji-twoslash` transformer](https://shikiji.netlify.app/packages/twoslash).

```ts
import { createTwoSlashFromCDN } from 'twoslash-cdn'
import { createHighlighter } from 'shikiji'
import { transformerTwoSlash } from 'shikiji-twoslash'

const highlighter = await createHighlighter({})

const twoslash = createTwoSlashFromCDN()

const code = `
import { ref } from 'vue'
const foo = ref(1)
// ^?
`

// Load all necessary types from CDN before hand
await twoslash.prepreTypes(code)

// This can be done synchronously
const highlighted = highlighter.codeToHtml(code, {
lang: 'ts',
theme: 'dark-plus',
transformers: [
transformerTwoSlash({
// Use `twoslash.runSync` to replace the non-CDN `twoslasher` function
twoslasher: twoslash.runSync
})
],
})
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"simple-git-hooks": "^2.9.0",
"tslib": "^2.6.2",
"twoslash": "workspace:*",
"twoslash-cdn": "workspace:*",
"twoslash-vue": "workspace:*",
"typescript": "^5.3.3",
"unbuild": "^2.0.0",
Expand Down
19 changes: 19 additions & 0 deletions packages/twoslash-cdn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# twoslash-cdn

[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]

Run TwoSlash on the browsers or web workers, with [Auto-Type-Acquisition](https://www.typescriptlang.org/play#example/automatic-type-acquisition) from CDN.

[📚 Documentation](https://twoslash.netlify.app/packages/cdn)

## License

[MIT](./LICENSE) License © 2023-PRESENT [Anthony Fu](https://github.com/antfu)

<!-- Badges -->

[npm-version-src]: https://img.shields.io/npm/v/twoslash-cdn?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/twoslash-cdn
[npm-downloads-src]: https://img.shields.io/npm/dm/twoslash-cdn?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/twoslash-cdn
13 changes: 13 additions & 0 deletions packages/twoslash-cdn/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [
'src/index',
],
declaration: true,
clean: true,
rollup: {
emitCJS: false,
inlineDependencies: true,
},
})
86 changes: 86 additions & 0 deletions packages/twoslash-cdn/examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<!doctype html>
<html lang="en">
<!-- Check this file live: https://twoslash-cdn-examples.netlify.app/ -->
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TwoSlash + Shikiji on CDN Example</title>
<link rel="stylesheet" href="https://esm.sh/shikiji-twoslash/style-rich.css" />
<style>
html {
color-scheme: dark;
font-family: 'Inter', sans-serif;
}
pre.twoslash {
--twoslash-popup-bg: #222;
}
body {
padding: 2em;
}
a {
color: #58a6ff;
}
</style>
</head>

<body>
<p>
This is an example of running
<a href="https://github.com/antfu/twoslash-cdn" target="_blank"> TwoSlash </a>
+ <a href="https://shikiji.netlify.app/" target="_blank">Shikiji</a> on CDN. This page is
<a href="https://github.com/antfu/twoslash-cdn/blob/main/examples/index.html" target="_blank">
a single static HTML file</a
>.
</p>
<div id="app">Loading...</div>
<script type="module">
// TODO: Replace with explicit versions in production
import { createTransformerFactory, rendererRich } from 'https://esm.sh/shikiji-twoslash@latest/core'
import { codeToHtml } from 'https://esm.sh/shikiji@latest'
import { createStorage } from 'https://esm.sh/unstorage@latest'
import indexedDbDriver from 'https://esm.sh/unstorage@latest/drivers/indexedb'
import { createTwoSlashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'

// ============= Initialization =============

// An example of using unstorage with IndexedDB to cache the virtual file system
const storage = createStorage({
driver: indexedDbDriver({ base: 'twoslash-cdn' }),
})

const twoslash = createTwoSlashFromCDN({
storage,
compilerOptions: {
lib: ['esnext', 'dom'],
},
})

const transformerTwoSlash = createTransformerFactory(twoslash.runSync)({
renderer: rendererRich(),
})

// ============= Execution =============

const app = document.getElementById('app')

const source = `
import { ref } from '@vue/reactivity'

console.log("Hi! Shikiji + TwoSlash on CDN :)")

const count = ref(0)
// ^?
`.trim()

// Before rendering, we need to prepare the types, so that the rendering could happend synchronously
await twoslash.prepareTypes(source)

// Then we can render the code
app.innerHTML = await codeToHtml(source, {
lang: 'ts',
theme: 'vitesse-dark',
transformers: [transformerTwoSlash],
})
</script>
</body>
</html>
64 changes: 64 additions & 0 deletions packages/twoslash-cdn/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "twoslash-cdn",
"type": "module",
"version": "0.0.2",
"description": "Run TwoSlash on the browser, with Auto-Type-Acquisition on CDN.",
"author": "Anthony Fu <[email protected]>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/twoslashes/twoslash#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/twoslashes/twoslash.git",
"directory": "packages/twoslash-cdn"
},
"bugs": "https://github.com/twoslashes/twoslash/issues",
"keywords": [
"typescript",
"twoslash"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./dist/index.d.ts"
]
}
},
"files": [
"dist"
],
"scripts": {
"play": "nr -C playground dev",
"build": "unbuild",
"dev": "unbuild --stub",
"lint": "eslint .",
"prepublishOnly": "nr build"
},
"peerDependencies": {
"typescript": "*"
},
"dependencies": {
"twoslash": "workspace:*"
},
"devDependencies": {
"@typescript/ata": "^0.9.4",
"@typescript/vfs": "^1.5.0",
"twoslash-cdn": "workspace:*",
"typescript": "^5.3.3",
"unbuild": "^2.0.0",
"unstorage": "^1.10.1",
"vite": "^5.0.10",
"vitest": "^1.0.4"
}
}
24 changes: 24 additions & 0 deletions packages/twoslash-cdn/playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
24 changes: 24 additions & 0 deletions packages/twoslash-cdn/playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TwoSlash + Shikiji on CDN Example</title>
<style>
:root {
color-scheme: dark;
}
pre.twoslash {
--twoslash-popup-bg: #222;
}
#app {
padding: 2em;
}
</style>
</head>

<body>
<div id="app">Loading...</div>
<script type="module" src="./main.ts"></script>
</body>
</html>
Loading