@bigmistqke/repl
provides unstyled building blocks to create TypeScript playgrounds directly in the browser, featuring adaptable editor integration. Currently, it supports both the feature-rich Monaco Editor
and the lighter Tm Editor
. It supports real-time transpilation of TypeScript into ECMAScript Modules (ESM) and facilitates seamless imports of external dependencies, including their type definitions, making it ideal for both quick prototyping and complex browser-based IDE development.
Screen.Recording.2024-04-29.at.22.46.58.mov
Click here for a line-by-line explanation of the above example and here for a live-demo.
- Modular Editor Integration: Start with
Monaco Editor
for a fully featured IDE-like experience orTm Editor
for a more minimal editor. The architecture is designed to accommodate additional editors as needed. - Real-time Transpilation: Transpile TypeScript into ESM on-the-fly, enabling immediate feedback and iteration.
- Automatic Dependency Management: Effortlessly manage imports of external libraries and their associated types, streamlining the development process.
- Configurable and Extensible: Tailor the setup to your needs with configurable TypeScript compiler settings, and easily extend functionalities through a flexible API.
Import package from package manager.
npm install `@bigmistqke/repl`
yarn add `@bigmistqke/repl`
pnpm install `@bigmistqke/repl`
The @bigmistqke/repl
library is designed to be modular, allowing users to import only the parts they need for their project from a CDN if they would like to. Below is a list of all available modules within the @bigmistqke/repl
ecosystem:
- @bigmistqke/repl: Contains core runtime functionalities necessary for creating and managing the virtual environment within the repl.
- @bigmistqke/repl/element: Entry to the core custom elements
- @bigmistqke/repl/element/monaco-editor: Integrates the Monaco editor, providing a rich coding environment.
- @bigmistqke/repl/element/tm-editor: Adds syntax highlighting through
tm-textarea
- @bigmistqke/repl/plugins/rollup-service-worker: Enhances the repl with service worker capabilities to efficiently manage caching.
- @bigmistqke/repl/plugins/babel-solid-repl: Provides plugins for Babel that are optimized for SolidJS applications.
- @bigmistqke/repl/transform/babel: Supports the integration of Babel for dynamic JavaScript code transformation.
- @bigmistqke/repl/transform/typescript: Allows for the inclusion and use of the TypeScript compiler for code transformation.
- @bigmistqke/repl/transform-module-paths/typescript: Offers utilities for managing and transforming module paths in TypeScript files, crucial for resolving imports and integrating external modules.
This straightforward example showcases how to set up and utilize the @bigmistqke/repl for a simple TypeScript computation directly within the browser. It illustrates the essential functionality of loading a single file, transpiling TypeScript, and executing a basic function, making it an ideal starting point for those new to integrating the REPL in their web applications.
import { Repl, useRuntime } from '@bigmistqke/repl';
import { typescriptTransform } from '@bigmistqke/repl/transform/typescript';
import { typescriptTransformModulePaths } from '@bigmistqke/repl/transform-module-paths/typescript';
const initialState = {
files: {
sources: {
'src/index.ts': `
export const sum = (a: number, b: number): number => a + b;
`,
},
},
};
export default function App() {
function onSetup({ fileSystem }: Runtime) {
const file = fileSystem.get('src/index.ts');
// Get esm-url from file
const moduleUrl = file?.module.url;
// Import module and call its function
import(moduleUrl).then(module => {
console.info('Sum of 1 and 2 is:', module.sum(1, 2));
});
}
return (
<Repl
initialState={initialState}
onSetup={onSetup}
transform={typescriptTransform({
typescript: import('https://esm.sh/typescript'),
})}
transformModulePaths={typescriptTransformModulePaths({
typescript: import('https://esm.sh/typescript'),
})}
/>
);
}
This advanced example demonstrates how to setup a solid playground. This example includes integrations with monaco-editor
, babel
, typescript
and the chrome devtools via chobitsu
.
import { DevTools, Frame, Repl } from '@bigmistqke/repl'
import { MonacoEditor, MonacoTheme } from '@bigmistqke/repl/solid/monaco-editor'
import { babelSolidReplPlugin } from '@bigmistqke/repl/plugins/babel-solid-repl'
import vs_dark from '@bigmistqke/repl/solid/monaco-editor/themes/vs_dark_good.json'
import { typescriptTransformModulePaths } from '@bigmistqke/repl/transform-module-paths/typescript'
import { babelTransform } from '@bigmistqke/repl/transform/babel'
import loader from '@monaco-editor/loader'
import { createEffect, mapArray } from 'solid-js'
export default function App() {
return (
<Repl
importExternalTypes
transformModulePaths={
typescriptTransformModulePaths(import('https://esm.sh/typescript'))
}
transform={
babelTransform({
babel: import('https://esm.sh/@babel/standalone'),
presets: ['babel-preset-solid'],
plugins: [babelSolidReplPlugin],
})
}
initialState={{
files: {
sources: {
'index.css': `body { background: blue; }`,
'index.tsx': `import { render } from "solid-js/web";
import { createSignal } from "solid-js";
import "solid-js/jsx-runtime";
import "./index.css";
const [count, setCount] = createSignal(1);
render(() => (
<button onClick={() => setCount(count => count + 1)}>
{count()}
</button>
), document.body);
`,
},
},
}}
onSetup={({ fileSystem, frameRegistry }) {
createEffect(() => {
// Get the default frame from the frame registry
const frame = frameRegistry.get('default')
if (!frame) return
// Get file in virtual filesystem that points to 'index.tsx'
const entry = fileSystem.get('index.tsx')
// Inject the entry's module URL into the frame's window
createEffect(() => frame.injectFile(entry))
// Inject the css-imports from the entry-file into the frame's window.
createEffect(
mapArray(entry.module.cssImports, css => {
createEffect(() => frame.injectFile(css))
onCleanup(() => frame.dispose(css))
}),
)
})
}
return (
<Repl
importExternalTypes
transformModulePaths={typescriptTransformModulePaths({
typescript: import('https://esm.sh/typescript')
})}
transform={babelTransform({
babel: import('https://esm.sh/@babel/standalone'),
presets: ['babel-preset-solid'],
plugins: [babelSolidReplPlugin],
})}
initialState={initialState}
onSetup={onSetup}
>
<MonacoEditor
monaco={loader.init()}
theme={vs_dark as MonacoTheme}
tsconfig={{
target: 2, // ScriptTarget.ES2015
module: 5, // ModuleKind.ES2015
jsx: 1, // JsxEmit.Preserve
jsxImportSource: 'solid-js',
esModuleInterop: true,
}}
path='index.tsx'
/>
<Frame />
<DevTools />
</Repl>
)
}