Skip to content

Commit de0c6fb

Browse files
authored
perf (#57)
* perf * perf refresh * perf * rm gitignore * redesign website * fix compatibility * editor highlighting * separate worker * update pnpm * ci * update pnpm configs * update pnpm configs * add core * remove gl
1 parent 194d978 commit de0c6fb

23 files changed

Lines changed: 2157 additions & 1983 deletions

.github/workflows/ci.yml

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,31 @@ on:
1111
jobs:
1212
install:
1313
runs-on: ubuntu-latest
14-
timeout-minutes: 5
14+
timeout-minutes: 10
1515

1616
name: Node
1717
steps:
1818
- name: Checkout Code
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v7
2020

21-
- uses: pnpm/action-setup@v4
21+
- uses: pnpm/action-setup@v6
2222

23-
- name: Use Node 18 and pnpm
24-
uses: actions/setup-node@v4
23+
- name: Use Node 24 and pnpm
24+
uses: actions/setup-node@v6
2525
with:
26-
node-version: 20
26+
node-version: 24
2727
cache: 'pnpm'
2828
registry-url: 'https://registry.npmjs.org'
2929

30+
- name: Use Bun
31+
uses: oven-sh/setup-bun@v2
32+
3033
- name: Install Dependencies
3134
run: pnpm install --frozen-lockfile
3235

36+
- name: Build Site
37+
run: pnpm run build:site
38+
3339
release:
3440
runs-on: ubuntu-latest
3541
needs: [install]
@@ -39,17 +45,26 @@ jobs:
3945

4046
steps:
4147
- name: Checkout Code
42-
uses: actions/checkout@v4
48+
uses: actions/checkout@v7
4349

44-
- uses: pnpm/action-setup@v4
50+
- uses: pnpm/action-setup@v6
4551

46-
- name: Use Node 18 and pnpm
47-
uses: actions/setup-node@v4
52+
- name: Use Node 24 and pnpm
53+
uses: actions/setup-node@v6
4854
with:
49-
node-version: 20
55+
node-version: 24
5056
cache: 'pnpm'
5157
registry-url: 'https://registry.npmjs.org'
5258

59+
- name: Use Bun
60+
uses: oven-sh/setup-bun@v2
61+
62+
- name: Install Dependencies
63+
run: pnpm install --frozen-lockfile
64+
65+
- name: Build Package
66+
run: pnpm run build
67+
5368
- name: Determine tag
5469
id: determine_tag
5570
run: |
@@ -69,5 +84,4 @@ jobs:
6984
if: steps.determine_tag.outputs.tag == ''
7085
run: |
7186
echo "Publishing to latest"
72-
pnpm install --frozen-lockfile
73-
pnpm publish --no-git-check
87+
pnpm publish --no-git-check

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22
node_modules
33
dist
44
.DS_Store
5+
next-env.d.ts
6+
tsconfig.tsbuildinfo
7+
.pnpm-store
8+
59

API.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Devjar API
2+
3+
Devjar runs editable React code in a browser iframe. The runtime renders the default export from `index.js`.
4+
5+
Devjar requires React 19.
6+
7+
## Install
8+
9+
```sh
10+
pnpm add devjar
11+
```
12+
13+
## React Runtime
14+
15+
### `<DevJar />`
16+
17+
```tsx
18+
import { DevJar } from 'devjar'
19+
20+
const files = {
21+
'index.js': `export default function App() {
22+
return <h1>Hello world</h1>
23+
}`,
24+
}
25+
26+
export function Preview() {
27+
return (
28+
<DevJar
29+
files={files}
30+
resolveModule={(specifier) => `https://esm.sh/${specifier}`}
31+
/>
32+
)
33+
}
34+
```
35+
36+
Props:
37+
38+
- `files: Record<string, string>`: files available to the runtime. `index.js` is the entry file.
39+
- `resolveModule?: (specifier: string) => string`: maps bare imports such as `react` or `lucide-react` to browser-loadable module URLs.
40+
- `onError?: (...data: any[]) => void`: receives runtime or transform errors.
41+
- `tailwindSrc?: string | false`: Tailwind browser script URL injected into the iframe. Defaults to `https://unpkg.com/@tailwindcss/browser@4`; pass `false` to disable it.
42+
43+
File keys can include relative modules and CSS:
44+
45+
```ts
46+
const files = {
47+
'index.js': `import './styles.css'
48+
export default function App() {
49+
return <button className="button">Save</button>
50+
}`,
51+
'./styles.css': `.button { font: inherit; }`,
52+
}
53+
```
54+
55+
### `useLiveCode(options)`
56+
57+
Lower-level hook used by `<DevJar />`.
58+
59+
```tsx
60+
import { useLiveCode } from 'devjar'
61+
import { useEffect } from 'react'
62+
63+
export function Preview({ files }) {
64+
const { ref, error, load } = useLiveCode({
65+
resolveModule: (specifier) => `https://esm.sh/${specifier}`,
66+
})
67+
68+
useEffect(() => {
69+
load(files)
70+
}, [files, load])
71+
72+
return <iframe ref={ref} />
73+
}
74+
```
75+
76+
Options:
77+
78+
- `resolveModule?: (specifier: string) => string`: maps bare imports to browser-loadable module URLs.
79+
- `tailwindSrc?: string | false`: Tailwind browser script URL injected into the iframe. Defaults to `https://unpkg.com/@tailwindcss/browser@4`; pass `false` to disable it.
80+
81+
Returns:
82+
83+
- `ref`: iframe ref for the runtime.
84+
- `error`: latest runtime error.
85+
- `load(files)`: loads and executes a file map.

README.md

Lines changed: 9 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
devjar is a library that enables you to live test and share your code snippets and examples with others. devjar will generate a live code editor where you can run your code snippets and view the results in real-time based on the provided code content of your React app.
99

10-
**Notice:** devjar only works for browser runtime at the moment. It will always render the default export component in `index.js` as the app entry.
10+
**Notice:** devjar requires React 19 and only works for browser runtime at the moment. It will always render the default export component in `index.js` as the app entry.
1111

1212
## Install
1313

@@ -24,8 +24,9 @@ pnpm add devjar
2424
**Props**
2525

2626
* `files`: An object that specifies the files you want to include in your development environment.
27-
* `getModuleUrl`: A function that maps module names to CDN URLs.
27+
* `resolveModule`: A function that maps module specifiers to browser-loadable module URLs.
2828
* `onError`: Callback function of error event from the iframe sandbox. By default `console.log`.
29+
* `tailwindSrc`: Optional Tailwind browser script URL. Pass `false` to disable Tailwind injection.
2930

3031
**Example**
3132

@@ -42,8 +43,8 @@ function App() {
4243
return (
4344
<DevJar
4445
files={files}
45-
getModuleUrl={(m) => {
46-
return `${CDN_HOST}/${m}`
46+
resolveModule={(specifier) => {
47+
return `${CDN_HOST}/${specifier}`
4748
}}
4849
/>
4950
)
@@ -57,7 +58,8 @@ A hook that provides lower-level control over the live code execution environmen
5758
**Parameters**
5859

5960
* `options`
60-
* `getModulePath(module)`: A function that receives the module name and returns the CDN url of each imported module path. For example, import React from 'react' will load React from skypack.dev/react.
61+
* `resolveModule(specifier)`: A function that receives a module specifier and returns the browser-loadable URL. For example, import React from 'react' will load React from skypack.dev/react.
62+
* `tailwindSrc`: Optional Tailwind browser script URL. Pass `false` to disable Tailwind injection.
6163

6264
**Returns**
6365

@@ -75,8 +77,8 @@ function Playground() {
7577
const { ref, error, load } = useLiveCode({
7678
// The CDN url of each imported module path in your code
7779
// e.g. `import React from 'react'` will load react from skypack.dev/react
78-
getModulePath(modPath) {
79-
return `https://cdn.skypack.dev/${modPath}`
80+
resolveModule(specifier) {
81+
return `https://cdn.skypack.dev/${specifier}`
8082
}
8183
})
8284

@@ -106,77 +108,6 @@ function Playground() {
106108
}
107109
```
108110

109-
## GLSL Shader Runtime
110-
111-
### `useGL(options)`
112-
113-
A hook that renders GLSL fragment shaders using WebGL. Perfect for creating interactive shader playgrounds and visualizations.
114-
115-
**Parameters**
116-
117-
* `options`
118-
* `fragment`: The GLSL fragment shader source code as a string.
119-
* `canvasRef`: A React ref to an HTML canvas element where the shader will be rendered.
120-
* `onError`: Optional callback function that receives error messages (prefixed with `devjar:gl`).
121-
122-
**Available Uniforms**
123-
124-
The hook automatically provides these uniforms to your fragment shader:
125-
126-
* `u_time`: `float` - Elapsed time in seconds since the renderer started
127-
* `u_resolution`: `vec2` - Canvas dimensions (width, height)
128-
* `u_mouse`: `vec2` - Normalized mouse position (0.0 to 1.0)
129-
130-
**Example**
131-
132-
```jsx
133-
import { useGL } from 'devjar'
134-
import { useRef, useState } from 'react'
135-
136-
function ShaderPlayground() {
137-
const canvasRef = useRef(null)
138-
const [shaderCode, setShaderCode] = useState(`
139-
precision mediump float;
140-
141-
uniform vec2 u_resolution;
142-
uniform float u_time;
143-
uniform vec2 u_mouse;
144-
145-
void main() {
146-
vec2 st = gl_FragCoord.xy / u_resolution.xy;
147-
vec3 color = vec3(st.x, st.y, abs(sin(u_time)));
148-
gl_FragColor = vec4(color, 1.0);
149-
}
150-
`)
151-
const [error, setError] = useState(null)
152-
153-
useGL({
154-
fragment: shaderCode,
155-
canvasRef,
156-
onError: setError
157-
})
158-
159-
return (
160-
<div>
161-
<textarea
162-
value={shaderCode}
163-
onChange={(e) => setShaderCode(e.target.value)}
164-
style={{ width: '100%', height: '200px' }}
165-
/>
166-
<canvas ref={canvasRef} style={{ width: '100%', height: '300px' }} />
167-
{error && <pre style={{ color: 'red' }}>{error}</pre>}
168-
</div>
169-
)
170-
}
171-
```
172-
173-
**Error Handling**
174-
175-
All errors are prefixed with `devjar:gl` for easy identification:
176-
- `devjar:gl Shader compilation error: ...`
177-
- `devjar:gl Program linking error: ...`
178-
- `devjar:gl WebGL is not supported in your browser`
179-
180111
## License
181112

182113
The MIT License (MIT).

package.json

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,48 @@
33
"version": "0.8.0",
44
"type": "module",
55
"exports": {
6-
".": "./dist/index.js",
7-
"./gl": {
8-
"types": "./dist/gl.d.ts",
9-
"default": "./dist/gl.js"
10-
},
11-
"./package.json": "./package.json"
6+
".": {
7+
"types": "./dist/index.d.ts",
8+
"default": "./dist/index.js"
9+
}
1210
},
1311
"license": "MIT",
1412
"files": [
1513
"dist"
1614
],
1715
"types": "./dist/index.d.ts",
1816
"scripts": {
19-
"build": "bunchee",
17+
"build": "pnpm run build:lib && pnpm run build:worker",
18+
"build:lib": "bunchee",
19+
"build:worker": "bun build src/transform-worker.ts --outfile dist/transform-worker.js --target browser --format esm",
2020
"prepublishOnly": "pnpm run build",
2121
"build:site": "pnpm run build && next build ./site",
2222
"start": "next start ./site",
23-
"dev": "next dev ./site"
23+
"dev": "pnpm run build && (pnpm run dev:lib & pnpm run dev:worker & pnpm run dev:site & wait)",
24+
"dev:lib": "bunchee --watch",
25+
"dev:worker": "bun build src/transform-worker.ts --outfile dist/transform-worker.js --target browser --format esm --watch",
26+
"dev:site": "next dev ./site"
2427
},
2528
"peerDependencies": {
26-
"react": "^18.2.0 || ^19.0.0"
29+
"react": "^19.0.0"
2730
},
2831
"dependencies": {
29-
"dedent": "^1.7.0",
3032
"es-module-lexer": "1.6.0",
31-
"es-module-shims": "2.0.3",
32-
"sucrase": "3.35.0"
33+
"oxc-transform": "^0.137.0",
34+
"react-refresh": "^0.18.0"
3335
},
3436
"devDependencies": {
3537
"@types/node": "^22.10.7",
3638
"@types/react": "^19.0.7",
3739
"@types/react-dom": "^19.0.3",
38-
"bunchee": "^6.3.3",
39-
"codice": "^1.5.4",
40+
"bunchee": "^6.11.0",
41+
"codice": "^1.6.0",
42+
"dedent": "^1.7.0",
4043
"devjar": "link:",
41-
"next": "^16.0.7",
44+
"next": "16.3.0-canary.69",
4245
"react": "^19.2.1",
4346
"react-dom": "^19.2.1",
4447
"typescript": "^5.7.3"
4548
},
46-
"packageManager": "pnpm@9.15.4"
49+
"packageManager": "pnpm@11.9.0"
4750
}

0 commit comments

Comments
 (0)