├── .prettierignore
├── dev
├── tsconfig.json
├── index.ts
├── index.html
├── index.css
├── vite.config.ts
├── app.tsx
└── lib
│ ├── repl-toolkit.d.ts
│ └── repl-toolkit.js
├── .gitignore
├── .vscode
└── extensions.json
├── .formatignore
├── .editorconfig
├── .claude
└── settings.local.json
├── .prettierrc
├── env.d.ts
├── tsconfig.node.json
├── node
└── get-module-dependencies-demo.ts
├── src
├── types.ts
├── index.ts
├── extension-presets
│ ├── html-extension.ts
│ └── js-extension.ts
├── core
│ ├── create-file-url.ts
│ ├── default-file-url-system.ts
│ └── create-file-url-system.ts
├── utils
│ ├── transform-babel.ts
│ ├── transform-html-worker.ts
│ ├── transform-html.ts
│ ├── path-utils.ts
│ ├── get-module-path-ranges.ts
│ ├── monaco
│ │ ├── bind-monaco-to-file-system.ts
│ │ └── create-monaco-typeloader.ts
│ ├── resolve-package-entries.ts
│ ├── transform-module-paths.ts
│ ├── get-module-dependencies.ts
│ ├── download-types.ts
│ └── utils.ts
└── frame
│ └── frame-utils.ts
├── tsconfig.json
├── .github
├── ISSUE_TEMPLATE
│ ├── feature-request.yml
│ └── bug-report.yml
└── workflows
│ ├── format.yml
│ ├── deploy.yml
│ ├── publish.yml
│ └── codeql.yml
├── LICENSE
├── vite.config.ts
├── eslint.config.js
├── vite.config.demo.ts
├── test
├── path-utils.test.ts
├── get-module-path-ranges.test.ts
└── get-module-dependencies.test.ts
├── package.json
└── README.md
/.prettierignore:
--------------------------------------------------------------------------------
1 | README.md
2 | dev/lib/**/*
--------------------------------------------------------------------------------
/dev/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "include": ["."]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | gitignore
3 | dist
4 |
5 | # tsup
6 | tsup.config.bundled_*.{m,c,}s
7 | .vercel
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
3 | }
4 |
--------------------------------------------------------------------------------
/dev/index.ts:
--------------------------------------------------------------------------------
1 | import { render } from 'solid-js/web'
2 | import { App } from './app'
3 |
4 | render(App, document.getElementById('root')!)
5 |
--------------------------------------------------------------------------------
/.formatignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | gitignore
3 | dist
4 | dev/lib
5 |
6 | # tsup
7 |
8 | tsup.config.bundled\_\*.{m,c,}s
9 | .vercel
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.claude/settings.local.json:
--------------------------------------------------------------------------------
1 | {
2 | "permissions": {
3 | "allow": [
4 | "Bash(grep:*)",
5 | "Bash(pnpm add:*)",
6 | "Bash(pnpm test:*)",
7 | "Bash(npx tsx:*)"
8 | ],
9 | "deny": []
10 | }
11 | }
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "tabWidth": 2,
4 | "printWidth": 100,
5 | "semi": false,
6 | "singleQuote": true,
7 | "useTabs": false,
8 | "arrowParens": "avoid",
9 | "bracketSpacing": true
10 | }
11 |
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | interface ImportMeta {
3 | env: {
4 | NODE_ENV: 'production' | 'development'
5 | PROD: boolean
6 | DEV: boolean
7 | }
8 | }
9 | namespace NodeJS {
10 | interface ProcessEnv {
11 | NODE_ENV: 'production' | 'development'
12 | PROD: boolean
13 | DEV: boolean
14 | }
15 | }
16 | }
17 |
18 | export {}
19 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Repl
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // General
4 | "jsx": "preserve",
5 | "jsxImportSource": "solid-js",
6 | "target": "ESNext",
7 |
8 | // Modules
9 | "allowSyntheticDefaultImports": true,
10 | "allowImportingTsExtensions": true,
11 | "esModuleInterop": true,
12 | "isolatedModules": true,
13 | "module": "ESNext",
14 | "moduleResolution": "bundler",
15 | "noEmit": true,
16 |
17 | // Type Checking & Safety
18 | "strict": true,
19 | "types": ["node"]
20 | },
21 | "include": ["node"]
22 | }
23 |
--------------------------------------------------------------------------------
/node/get-module-dependencies-demo.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs/promises'
2 | import * as ts from 'typescript'
3 | import { getModuleDependencies } from '../src/utils/get-module-dependencies.ts'
4 | import { resolvePath } from '../src/utils/path-utils.ts'
5 |
6 | console.log(
7 | await getModuleDependencies({
8 | entry: '../dev/App.tsx',
9 | readFile: path => {
10 | const resolvedPath = resolvePath(import.meta.url, path).replace('file://', '')
11 | return fs.readFile(resolvedPath, {
12 | encoding: 'utf-8',
13 | })
14 | },
15 | ts,
16 | }),
17 | )
18 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js'
2 |
3 | export type AccessorMaybe = T | Accessor
4 |
5 | export type FileType = 'javascript' | 'css' | 'html' | 'wasm' | 'plain'
6 | export interface Extension {
7 | transform?: Transform
8 | type: FileType
9 | }
10 | export interface FileUrlSystem {
11 | invalidate(path: string): void
12 | get(path: string, config?: { cached?: boolean }): string | undefined
13 | }
14 | export interface TransformConfig {
15 | path: string
16 | source: string
17 | fileUrls: FileUrlSystem
18 | }
19 | export type Transform = (config: TransformConfig) => Accessor | string
20 |
--------------------------------------------------------------------------------
/dev/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | box-sizing: border-box;
9 | margin: 0;
10 | padding: 20px;
11 | height: 100vh;
12 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
13 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
14 | }
15 |
16 | #root {
17 | display: grid;
18 | grid-template-rows: auto 1fr 1fr;
19 | gap: 5px;
20 | height: 100%;
21 | overflow: hidden;
22 | }
23 |
24 | .buttons {
25 | display: flex;
26 | align-content: start;
27 | gap: 5px;
28 | }
29 |
30 | iframe {
31 | border: 1px solid black;
32 | width: 100%;
33 | height: 100%;
34 | }
35 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './core/create-file-url-system.ts'
2 | export * from './core/create-file-url.ts'
3 | export * from './core/default-file-url-system.ts'
4 | export * from './extension-presets/html-extension.ts'
5 | export * from './extension-presets/js-extension.ts'
6 | export * from './types.ts'
7 | export * from './utils/download-types.ts'
8 | export * from './utils/get-module-dependencies.ts'
9 | export * from './utils/get-module-path-ranges.ts'
10 | export * from './utils/monaco/create-monaco-typeloader.ts'
11 | export * as PathUtils from './utils/path-utils.ts'
12 | export * from './utils/resolve-package-entries.ts'
13 | export * from './utils/transform-babel.ts'
14 | export * from './utils/transform-html-worker.ts'
15 | export * from './utils/transform-html.ts'
16 | export * from './utils/transform-module-paths.ts'
17 |
--------------------------------------------------------------------------------
/src/extension-presets/html-extension.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js'
2 | import type { TransformConfig } from '../types.ts'
3 | import { transformHtmlWorker } from '../utils/transform-html-worker.ts'
4 | import { transformHtml } from '../utils/transform-html.ts'
5 |
6 | export interface HTMLExtensionConfig {
7 | transformModule: (config: TransformConfig) => Accessor
8 | }
9 |
10 | export function createHTMLExtension(config: HTMLExtensionConfig) {
11 | return {
12 | type: 'html' as const,
13 | transform(options: TransformConfig) {
14 | return transformHtml({ ...config, ...options })
15 | },
16 | }
17 | }
18 |
19 | export function createHTMLExtensionWorker(config: HTMLExtensionConfig) {
20 | return {
21 | type: 'html' as const,
22 | transform(options: TransformConfig) {
23 | return transformHtmlWorker({ ...config, ...options })
24 | },
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/core/create-file-url.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Creates an object URL representing a virtual text-based file.
3 | *
4 | * This function wraps a given text `source` into a `Blob` with a MIME type of `text/{type}`,
5 | * then returns an object URL that can be used as a file source (e.g., in an `