├── .gitignore ├── .babelrc ├── playground ├── vite.config.js ├── index.html └── app.tsx ├── rollup.config.js ├── .prettierrc ├── tsconfig.json ├── package.json ├── src └── solid-repl.tsx ├── README.md └── pnpm-lock.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .parcel-cache 4 | .cache -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-solid", "@babel/preset-typescript"] 3 | } -------------------------------------------------------------------------------- /playground/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import solidPlugin from 'vite-plugin-solid'; 3 | 4 | export default defineConfig({ 5 | plugins: [solidPlugin()], 6 | }); 7 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import defineConfig from 'rollup-preset-solid'; 2 | 3 | export default defineConfig({ 4 | input: 'src/solid-repl.tsx', 5 | external: ['@amoutonbrady/lz-string'], 6 | printInstructions: true, 7 | }); 8 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "endOfLine": "lf", 5 | "htmlWhitespaceSensitivity": "ignore", 6 | "jsxSingleQuote": false, 7 | "printWidth": 80, 8 | "quoteProps": "as-needed", 9 | "semi": true, 10 | "singleQuote": true, 11 | "trailingComma": "all", 12 | "tabWidth": 2, 13 | "useTabs": false 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "allowSyntheticDefaultImports": true, 6 | "esModuleInterop": true, 7 | "declaration": true, 8 | "moduleResolution": "node", 9 | "declarationDir": "./dist/types", 10 | "jsx": "preserve", 11 | "resolveJsonModule": true, 12 | "newLine": "lf", 13 | "outDir": "./dist/source", 14 | "jsxImportSource": "solid-js" 15 | }, 16 | "include": ["./src"], 17 | "exclude": ["node_modules/"] 18 | } 19 | -------------------------------------------------------------------------------- /playground/app.tsx: -------------------------------------------------------------------------------- 1 | import { Repl, ReplTab } from '..'; 2 | import { render } from 'solid-js/web'; 3 | 4 | const App = () => { 5 | return ( 6 | <> 7 | 11 | 12 | 13 | {` 14 | const App = () =>

Hello world!

15 | `} 16 |
17 |
18 | 19 | ); 20 | }; 21 | 22 | render(App, document.getElementById('app')!); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solid-repl", 3 | "description": "A REPL for SolidJS", 4 | "author": "Alexandre Mouton-Brady", 5 | "license": "MIT", 6 | "version": "0.6.0", 7 | "homepage": "https://github.com/solidui/solid-repl#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/solidui/solid-repl.git" 11 | }, 12 | "type": "module", 13 | "main": "dist/esm/solid-repl.js", 14 | "module": "dist/esm/solid-repl.js", 15 | "types": "dist/types/solid-repl.d.ts", 16 | "exports": { 17 | ".": { 18 | "solid": "./dist/source/solid-repl.jsx", 19 | "default": "./dist/esm/solid-repl.js" 20 | } 21 | }, 22 | "files": [ 23 | "dist" 24 | ], 25 | "scripts": { 26 | "build": "rollup -c", 27 | "dev": "rollup -c -w", 28 | "format": "prettier --write \"src/**/*.{jsx,tsx,ts,js,json}\"", 29 | "test": "vite playground", 30 | "prepublishOnly": "pnpm build" 31 | }, 32 | "peerDependencies": { 33 | "solid-js": "^0.26" 34 | }, 35 | "dependencies": { 36 | "@amoutonbrady/lz-string": "^0.0.1", 37 | "solid-js": "^0.26.5" 38 | }, 39 | "devDependencies": { 40 | "prettier": "^2.3.0", 41 | "rollup": "^2.47.0", 42 | "rollup-preset-solid": "^0.3.0", 43 | "typescript": "^4.2.4", 44 | "vite": "^2.3.2", 45 | "vite-plugin-solid": "^1.8.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/solid-repl.tsx: -------------------------------------------------------------------------------- 1 | import { Show } from 'solid-js/web'; 2 | import { JSX, createMemo, splitProps } from 'solid-js'; 3 | import { compressToURL } from '@amoutonbrady/lz-string'; 4 | 5 | function uid() { 6 | const [ts, rand] = [Date.now(), Math.random()].map((value) => 7 | value.toString(36), 8 | ); 9 | 10 | return (ts + rand).replace(/\./g, ''); 11 | } 12 | 13 | function childrensToArray(children: unknown | unknown[]): T[] { 14 | return [...(Array.isArray(children) ? children : [children])]; 15 | } 16 | 17 | function formatCode(code: string) { 18 | const lines = code.split('\n'); 19 | 20 | let mindent: number | null = null; 21 | let result = code.replace(/\\\n[ \t]*/g, '').replace(/\\`/g, '`'); 22 | 23 | for (const line of lines) { 24 | const m = line.match(/^(\s+)\S+/); 25 | 26 | if (!m) continue; 27 | const indent = m[1].length; 28 | mindent = mindent ? Math.min(mindent, indent) : indent; 29 | } 30 | 31 | if (mindent !== null) { 32 | result = lines 33 | .map((line) => (line[0] === ' ' ? line.slice(mindent) : line)) 34 | .join('\n'); 35 | } 36 | 37 | return result.trim().replace(/\\n/g, '\n'); 38 | } 39 | 40 | export const ReplTab = (props: { 41 | name: string; 42 | children?: unknown | unknown[]; 43 | }) => { 44 | const id = uid(); 45 | 46 | return createMemo(() => { 47 | const source = childrensToArray(props.children).join(''); 48 | 49 | return { 50 | id, 51 | name: props.name, 52 | source: formatCode(source), 53 | type: 'tsx', 54 | } as unknown as JSX.Element; 55 | }); 56 | }; 57 | 58 | export const Repl = (props: ReplOptions) => { 59 | const [internal, external] = splitProps(props, [ 60 | 'data', 61 | 'height', 62 | 'baseUrl', 63 | 'children', 64 | 'isInteractive', 65 | 'withHeader', 66 | 'edtiableTabs', 67 | 'withActionBar', 68 | 'layout', 69 | ]); 70 | 71 | const tabs = createMemo(() => { 72 | if (!internal.children) return []; 73 | return childrensToArray<() => Tab>(internal.children).map((tab) => tab()); 74 | }); 75 | 76 | const src = createMemo(() => { 77 | const url = new URL(internal.baseUrl || 'https://playground.solidjs.com'); 78 | 79 | if (!internal.withHeader) url.searchParams.set('noHeader', 'true'); 80 | if (!internal.isInteractive) url.searchParams.set('noInteractive', 'true'); 81 | if (internal.data) url.searchParams.set('data', internal.data); 82 | if (!internal.edtiableTabs) url.searchParams.set('noEditableTabs', 'true') 83 | if (!internal.withActionBar) url.searchParams.set('noActionBar', 'true') 84 | if (internal.layout === 'vertical') url.searchParams.set('isHorizontal', 'true') 85 | 86 | if (tabs().length) { 87 | url.hash = compressToURL(JSON.stringify(tabs())); 88 | } 89 | 90 | return url.toString(); 91 | }); 92 | 93 | return ( 94 | The REPL needs to have at least one tab

} 97 | > 98 |