Playground Imports

The playground (https://tjs-platform.web.app) and the local dev server both let you import external packages without a bundler or node_modules. There's no setup — just write the import. This page documents how it works and the CDN-hint syntax for power users.

Quick reference

// Plain bare import — JSDelivr `/+esm` by default (works for ESM-native
// and CJS packages alike; deps bundled inline)
import { tosi, elements } from 'tosijs'
import { debounce } from 'lodash-es'
import { marked } from 'marked'

// Versioned: pin a specific release (or range)
import { tosi } from 'tosijs@1.6.1'
import { tosi } from 'tosijs@^1.6'

// Subpath into a package
import { createRoot } from 'react-dom/client'
import { getAuth } from 'firebase/auth'

// Full URL — passes through unchanged. Useful for arbitrary hosts:
// your GitHub repo, a CodePen-style snippet, anything that serves ESM.
import { thing } from 'https://example.com/my-module.js'
import { x } from 'https://raw.githubusercontent.com/user/repo/main/file.js'

// CDN hints (escape hatch when default routing picks the wrong CDN)
import { useState } from 'esmsh/react@18'
import { tosi } from 'jsdelivr/tosijs'
import { preact } from 'unpkg/preact'
import { whatever } from 'github/user/repo@v1.2/dist/index.js'

How it works

  1. Source rewrite at transpile time (demo/src/imports.ts): bare specifiers like 'tosijs' get rewritten to '/tfs/tosijs'. Relative paths ('./foo'), absolute paths ('/foo'), and full URLs ('https://...') are left alone.
  2. Service worker intercept (demo/src/tfs-worker.js): the playground iframe is loaded from a same-origin /iframe/<sessionId> URL, so the TFS service worker controls all its fetches. When the iframe imports from /tfs/<spec>, the SW resolves it to a CDN URL, fetches the module, optionally rewrites the response body, and serves it back.
  3. Browser ESM loader does the rest — follows imports, dedupes by URL, evaluates modules.

Default CDN routing

By default, /tfs/<spec> resolves to JSDelivr's /+esm endpoint:

import 'tosijs'
  → /tfs/tosijs
  → https://cdn.jsdelivr.net/npm/tosijs@latest/+esm

JSDelivr's /+esm returns a self-contained Rollup-bundled ESM module. It works cleanly for ESM-native packages (tosijs, lodash-es) AND CJS packages (most things), inlines process polyfills, and bundles dependencies inline.

The bundle-inline behavior breaks one specific case: packages with peer dependencies that need to be the SAME instance across modules. React is the canonical example — react-dom and your code both need to use the same React, or hooks crash (Cannot read properties of null (reading 'useState')). For these, the SW has a small allowlist that routes through esm.sh instead, which dedupes by URL.

Current allowlist: react, react-dom. Easy to extend in the SW source.

CDN hints

When you want to override the default routing, prefix the spec with a CDN name. The first path segment is treated as the hint:

Hint Resolves to Use when
jsdelivr/<spec> https://cdn.jsdelivr.net/npm/<spec>/+esm Force JSDelivr (e.g. for a peer-dep package not on the allowlist)
esmsh/<spec> https://esm.sh/<spec> Force esm.sh (e.g. for a different React-like package needing peer-dep dedup)
unpkg/<spec> https://unpkg.com/<spec>?module UNPKG, returns ESM via ?module
github/<user>/<repo>[@ref]/<path> https://esm.sh/gh/<user>/<repo>[@ref]/<path> Load directly from a GitHub repo (esm.sh handles fetching + transformation)

The hint name occupies what would normally be the package name slot, so named imports work normally:

import { tosi, elements } from 'jsdelivr/tosijs@1.6.1'
import { useState } from 'esmsh/react'

Why esmsh and not esm: esm is a popular npm package (the legacy ESM loader for Node), and we don't want to clash with import 'esm'. jsdelivr, unpkg, and github aren't taken as bare top-level npm packages.

Full URLs

Anything starting with http:// or https:// passes through the rewriter unchanged. Use this for one-off cases:

// Your own deployed module
import { thing } from 'https://my.com/module.js'

// A GitHub raw URL (only works if the file is already valid ESM —
// raw GitHub doesn't transform CJS or resolve bare specifiers; for
// that use the `github/` hint, which routes through esm.sh)
import { x } from 'https://raw.githubusercontent.com/me/repo/main/dist/index.mjs'

// Any other CDN
import { y } from 'https://cdn.example.com/foo@1.0.0/+esm'

What lives where

Future: virtual modules

The SW architecture is set up to also serve user-saved modules at /vmod/<path> (or similar). This will let you "save" a snippet to a virtual endpoint and import from it elsewhere — the b8rjs pattern. Tracked in TODO.md under playground IDE features.

Cache and debugging

The SW caches CDN responses in a named Cache (tfs-v4-mixed-cdn). Bumping SW_VERSION in tfs-worker.js automatically clears old caches on activate.