Skip to content

Twoslash CDN

Run Twoslash on the browsers or web workers, with Auto-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 the TypeScript Website project.

CDN Example | Example Source File

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
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'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 Shiki, the codeToHtml function is synchronous as well as the @shikijs/twoslash transformer.

ts
import { createTwoslashFromCDN } from 'twoslash-cdn'
import { createHighlighter } from 'shiki'
import { transformerTwoslash } from '@shikijs/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
    })
  ],
})

Released under the MIT License.