devjar 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ Notice: devjar only works for browser runtime at the moment. It will always rend
13
13
  ### Install
14
14
 
15
15
  ```sh
16
- yarn add devjar
16
+ pnpm add devjar
17
17
  ```
18
18
 
19
19
 
@@ -0,0 +1,28 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ declare global {
5
+ interface Window {
6
+ esmsInitOptions: {
7
+ shimMode: boolean;
8
+ mapOverrides: boolean;
9
+ };
10
+ }
11
+ function importShim(url: string): Promise<any>;
12
+ }
13
+
14
+ declare function useLiveCode({ getModuleUrl }: {
15
+ getModuleUrl?: (name: string) => string;
16
+ }): {
17
+ ref: react.RefObject<any>;
18
+ error: undefined;
19
+ load: (files: any) => Promise<void>;
20
+ };
21
+
22
+ declare function DevJar({ files, getModuleUrl, onError, ...props }: {
23
+ files: Record<string, string>;
24
+ getModuleUrl?: (name: string) => string;
25
+ onError?: (...data: any[]) => void;
26
+ } & React.IframeHTMLAttributes<HTMLIFrameElement>): react_jsx_runtime.JSX.Element;
27
+
28
+ export { DevJar, useLiveCode };
package/dist/index.js ADDED
@@ -0,0 +1,315 @@
1
+ import { useRef, useState, useId, useEffect, useCallback } from 'react';
2
+ import { transform } from 'sucrase';
3
+ import { init, parse } from 'es-module-lexer';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // declare esmsInitOptions on global window
7
+ async function createModule(files, { getModuleUrl }) {
8
+ let currentImportMap;
9
+ let shim;
10
+ async function setupImportMap() {
11
+ if (shim) return shim;
12
+ window.esmsInitOptions = {
13
+ shimMode: true,
14
+ mapOverrides: true
15
+ };
16
+ shim = import(/* webpackIgnore: true */ getModuleUrl('es-module-shims'));
17
+ await shim;
18
+ }
19
+ function updateImportMap(imports) {
20
+ imports['react'] = getModuleUrl('react');
21
+ imports['react-dom'] = getModuleUrl('react-dom');
22
+ imports['react-dom/client'] = getModuleUrl('react-dom/client');
23
+ const script = document.createElement('script');
24
+ script.type = 'importmap-shim';
25
+ script.innerHTML = JSON.stringify({
26
+ imports
27
+ });
28
+ document.body.appendChild(script);
29
+ if (currentImportMap) {
30
+ currentImportMap.parentNode.removeChild(currentImportMap);
31
+ }
32
+ currentImportMap = script;
33
+ }
34
+ function createInlinedModule(code, type) {
35
+ if (type === 'css') return `data:text/css;utf-8,${encodeURIComponent(code)}`;
36
+ return `data:text/javascript;utf-8,${encodeURIComponent(code)}`;
37
+ }
38
+ await setupImportMap();
39
+ const imports = Object.fromEntries(Object.entries(files).map(([fileName, code])=>[
40
+ fileName,
41
+ createInlinedModule(code, fileName.endsWith('.css') ? 'css' : 'js')
42
+ ]));
43
+ updateImportMap(imports);
44
+ return self.importShim('index.js');
45
+ }
46
+
47
+ let esModuleLexerInit;
48
+ const isRelative = (s)=>s.startsWith('./');
49
+ function transformCode(_code, getModuleUrl, externals) {
50
+ const code = transform(_code, {
51
+ transforms: [
52
+ 'jsx',
53
+ 'typescript'
54
+ ]
55
+ }).code;
56
+ return replaceImports(code, getModuleUrl, externals);
57
+ }
58
+ function replaceImports(source, getModuleUrl, externals) {
59
+ let code = '';
60
+ let lastIndex = 0;
61
+ let hasReactImports = false;
62
+ const [imports] = parse(source);
63
+ const cssImports = [];
64
+ let cssImportIndex = 0;
65
+ // start, end, statementStart, statementEnd, assertion, name
66
+ imports.forEach(({ s, e, ss, se, a, n })=>{
67
+ code += source.slice(lastIndex, ss) // content from last import to beginning of this line
68
+ ;
69
+ // handle imports
70
+ if (n.endsWith('.css')) {
71
+ // Map './styles.css' -> '@styles.css', and collect it
72
+ const cssPath = `${'@' + n.slice(2)}`;
73
+ cssImports.push(cssPath);
74
+ } else {
75
+ code += source.substring(ss, s);
76
+ code += isRelative(n) ? '@' + n.slice(2) : externals.has(n) ? n : getModuleUrl(n);
77
+ code += source.substring(e, se);
78
+ }
79
+ lastIndex = se;
80
+ if (n === 'react') {
81
+ const statement = source.slice(ss, se);
82
+ if (statement.includes('React')) {
83
+ hasReactImports = true;
84
+ }
85
+ }
86
+ cssImports.forEach((cssPath)=>{
87
+ code += `\nimport sheet${cssImportIndex} from "${cssPath}" assert { type: "css" };\n`;
88
+ cssImportIndex++;
89
+ });
90
+ });
91
+ if (cssImports.length) {
92
+ code += `const __customStyleSheets = [`;
93
+ for(let i = 0; i < cssImports.length; i++){
94
+ code += `sheet${i}`;
95
+ if (i < cssImports.length - 1) {
96
+ code += `, `;
97
+ }
98
+ }
99
+ code += `];\n`;
100
+ code += `document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...__customStyleSheets];\n`;
101
+ }
102
+ code += source.substring(lastIndex);
103
+ if (!hasReactImports) {
104
+ code = `import React from 'react';\n${code}`;
105
+ }
106
+ return code;
107
+ }
108
+ function createRenderer(createModule_, getModuleUrl) {
109
+ let reactRoot;
110
+ async function render(files) {
111
+ const mod = await createModule_(files, {
112
+ getModuleUrl
113
+ });
114
+ const ReactMod = await self.importShim('react');
115
+ const ReactDOMMod = await self.importShim('react-dom/client');
116
+ const _jsx = ReactMod.createElement;
117
+ const root = document.getElementById('__reactRoot');
118
+ class ErrorBoundary extends ReactMod.Component {
119
+ componentDidCatch(error) {
120
+ this.setState({
121
+ error
122
+ });
123
+ }
124
+ render() {
125
+ if (this.state.error) {
126
+ return _jsx('div', null, this.state.error?.message);
127
+ }
128
+ return this.props.children;
129
+ }
130
+ constructor(props){
131
+ super(props);
132
+ this.state = {
133
+ error: null
134
+ };
135
+ }
136
+ }
137
+ if (!reactRoot) {
138
+ reactRoot = ReactDOMMod.createRoot(root);
139
+ }
140
+ const Component = mod.default;
141
+ const element = _jsx(ErrorBoundary, null, _jsx(Component));
142
+ reactRoot.render(element);
143
+ }
144
+ return render;
145
+ }
146
+ function createMainScript({ uid }) {
147
+ const code = `\
148
+ 'use strict';
149
+ const _createModule = ${createModule.toString()};
150
+ const _createRenderer = ${createRenderer.toString()};
151
+
152
+ const getModuleUrl = (m) => window.parent.__devjar__[globalThis.uid].getModuleUrl(m)
153
+
154
+ globalThis.uid = ${JSON.stringify(uid)};
155
+ globalThis.__render__ = _createRenderer(_createModule, getModuleUrl);
156
+ `;
157
+ return code;
158
+ }
159
+ function createEsShimOptionsScript() {
160
+ return `\
161
+ window.esmsInitOptions = {
162
+ polyfillEnable: ['css-modules', 'json-modules'],
163
+ onerror: error => console.log(error),
164
+ }`;
165
+ }
166
+ function useScript() {
167
+ return useRef(typeof window !== 'undefined' ? document.createElement('script') : null);
168
+ }
169
+ function createScript(scriptRef, { content, src, type } = {}) {
170
+ const script = scriptRef.current;
171
+ if (type) script.type = type;
172
+ if (content) {
173
+ script.src = `data:text/javascript;utf-8,${encodeURIComponent(content)}`;
174
+ }
175
+ if (src) {
176
+ script.src = src;
177
+ }
178
+ return script;
179
+ }
180
+ function useLiveCode({ getModuleUrl }) {
181
+ const iframeRef = useRef(null);
182
+ const [error, setError] = useState();
183
+ const rerender = useState({})[1];
184
+ const appScriptRef = useScript();
185
+ const esShimOptionsScriptRef = useScript();
186
+ const tailwindcssScriptRef = useScript();
187
+ const uid = useId();
188
+ // Let getModuleUrl executed on parent window side since it might involve
189
+ // variables that iframe cannot access.
190
+ useEffect(()=>{
191
+ if (!globalThis.__devjar__) {
192
+ globalThis.__devjar__ = {};
193
+ }
194
+ globalThis.__devjar__[uid] = {
195
+ getModuleUrl
196
+ };
197
+ return ()=>{
198
+ if (globalThis.__devjar__) {
199
+ delete globalThis.__devjar__[uid];
200
+ }
201
+ };
202
+ }, []);
203
+ useEffect(()=>{
204
+ const iframe = iframeRef.current;
205
+ if (!iframe || !iframe.contentDocument) return;
206
+ const doc = iframe.contentDocument;
207
+ const body = doc.body;
208
+ const div = document.createElement('div');
209
+ div.id = '__reactRoot';
210
+ const appScriptContent = createMainScript({
211
+ uid
212
+ });
213
+ const scriptOptionsContent = createEsShimOptionsScript();
214
+ const esmShimOptionsScript = createScript(esShimOptionsScriptRef, {
215
+ content: scriptOptionsContent
216
+ });
217
+ const appScript = createScript(appScriptRef, {
218
+ content: appScriptContent,
219
+ type: 'module'
220
+ });
221
+ const tailwindScript = createScript(tailwindcssScriptRef, {
222
+ src: 'https://cdn.tailwindcss.com'
223
+ });
224
+ body.appendChild(div);
225
+ body.appendChild(esmShimOptionsScript);
226
+ body.appendChild(appScript);
227
+ body.appendChild(tailwindScript);
228
+ return ()=>{
229
+ if (!iframe || !iframe.contentDocument) return;
230
+ body.removeChild(div);
231
+ body.removeChild(esmShimOptionsScript);
232
+ body.removeChild(appScript);
233
+ body.removeChild(tailwindScript);
234
+ };
235
+ }, []);
236
+ const load = useCallback(async (files)=>{
237
+ if (!esModuleLexerInit) {
238
+ await init;
239
+ esModuleLexerInit = true;
240
+ }
241
+ if (files) {
242
+ // { 'react', 'react-dom' }
243
+ const overrideExternals = new Set(Object.keys(files).filter((name)=>!isRelative(name) && name !== 'index.js'));
244
+ // Always share react as externals
245
+ overrideExternals.add('react');
246
+ overrideExternals.add('react-dom');
247
+ try {
248
+ /**
249
+ * transformedFiles
250
+ * {
251
+ * 'index.js': '...',
252
+ * '@mod1': '...',
253
+ * '@mod2': '...',
254
+ */ const transformedFiles = Object.keys(files).reduce((res, filename)=>{
255
+ const key = isRelative(filename) ? '@' + filename.slice(2) : filename;
256
+ if (filename.endsWith('.css')) {
257
+ res[key] = files[filename];
258
+ } else {
259
+ res[key] = transformCode(files[filename], getModuleUrl, overrideExternals);
260
+ }
261
+ return res;
262
+ }, {});
263
+ const iframe = iframeRef.current;
264
+ const script = appScriptRef.current;
265
+ if (iframe) {
266
+ const render = iframe.contentWindow.__render__;
267
+ if (render) {
268
+ render(transformedFiles);
269
+ } else {
270
+ // if render is not loaded yet, wait until it's loaded
271
+ script.onload = ()=>{
272
+ iframe.contentWindow.__render__(transformedFiles);
273
+ };
274
+ }
275
+ }
276
+ setError(undefined);
277
+ } catch (e) {
278
+ console.error(e);
279
+ setError(e);
280
+ }
281
+ }
282
+ rerender({});
283
+ }, []);
284
+ return {
285
+ ref: iframeRef,
286
+ error,
287
+ load
288
+ };
289
+ }
290
+
291
+ const defaultOnError = typeof window !== 'undefined' ? console.error : ()=>{};
292
+ function DevJar({ files, getModuleUrl, onError = defaultOnError, ...props }) {
293
+ const onErrorRef = useRef(onError);
294
+ const { ref, error, load } = useLiveCode({
295
+ getModuleUrl
296
+ });
297
+ useEffect(()=>{
298
+ onErrorRef.current(error);
299
+ }, [
300
+ error
301
+ ]);
302
+ // load code files and execute them as live code
303
+ useEffect(()=>{
304
+ load(files);
305
+ }, [
306
+ files
307
+ ]);
308
+ // Attach the ref to an iframe element for runtime of code execution
309
+ return /*#__PURE__*/ jsx("iframe", {
310
+ ...props,
311
+ ref: ref
312
+ });
313
+ }
314
+
315
+ export { DevJar, useLiveCode };
package/package.json CHANGED
@@ -1,36 +1,43 @@
1
1
  {
2
2
  "name": "devjar",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": "./lib/index.mjs",
6
+ ".": "./dist/index.js",
7
7
  "./package.json": "./package.json"
8
8
  },
9
9
  "license": "MIT",
10
10
  "files": [
11
- "lib"
11
+ "dist"
12
12
  ],
13
- "types": "./lib/index.d.ts",
13
+ "types": "./dist/index.d.ts",
14
14
  "scripts": {
15
- "build:site": "next build ./site",
15
+ "build": "bunchee",
16
+ "prepublishOnly": "pnpm run build",
17
+ "build:site": "pnpm run build && next build ./site",
16
18
  "start": "next start ./site",
17
19
  "dev": "next dev ./site"
18
20
  },
19
21
  "peerDependencies": {
20
- "react": "^18.2.0"
22
+ "react": "^18.2.0 || ^19.0.0"
21
23
  },
22
24
  "dependencies": {
23
- "es-module-lexer": "0.10.5",
24
- "es-module-shims": "1.5.9",
25
- "sucrase": "3.23.0"
25
+ "es-module-lexer": "1.6.0",
26
+ "es-module-shims": "2.0.3",
27
+ "sucrase": "3.35.0"
26
28
  },
27
29
  "devDependencies": {
28
- "codice": "latest",
30
+ "@types/node": "^22.10.7",
31
+ "@types/react": "^19.0.7",
32
+ "@types/react-dom": "^19.0.3",
33
+ "bunchee": "^6.3.2",
34
+ "codice": "^0.4.4",
29
35
  "devjar": "link:./",
30
- "lodash-es": "^4.17.21",
31
- "next": "^13.2.0",
32
- "react": "^18.2.0",
33
- "react-dom": "^18.2.0",
34
- "sugar-high": "^0.4.5"
35
- }
36
+ "next": "^15.1.5",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0",
39
+ "sugar-high": "^0.8.2",
40
+ "typescript": "^5.7.3"
41
+ },
42
+ "packageManager": "pnpm@9.15.4"
36
43
  }
package/lib/core.mjs DELETED
@@ -1,274 +0,0 @@
1
- import { useEffect, useCallback, useState, useId, useRef } from 'react'
2
- import { createModule } from './module.mjs'
3
- import { transform } from 'sucrase'
4
- import { init, parse } from 'es-module-lexer'
5
-
6
- let esModuleLexerInit
7
-
8
- const isRelative = s => s.startsWith('./')
9
-
10
- function transformCode(_code, getModuleUrl, externals) {
11
- const code = transform(_code, {
12
- transforms: ['jsx', 'typescript'],
13
- }).code
14
-
15
- return replaceImports(code, getModuleUrl, externals)
16
- }
17
-
18
- function replaceImports(source, getModuleUrl, externals) {
19
- let code = ''
20
- let lastIndex = 0
21
- let hasReactImports = false
22
- const [imports] = parse(source)
23
- const cssImports = []
24
- let cssImportIndex = 0
25
-
26
- // start, end, statementStart, statementEnd, assertion, name
27
- imports.forEach(({ s, e, ss, se, a, n }) => {
28
- let isCss = false
29
- code += source.slice(lastIndex, ss) // content from last import to beginning of this line
30
-
31
-
32
- // handle imports
33
- if (n.endsWith('.css')) {
34
- isCss = true
35
- // Map './styles.css' -> '@styles.css', and collect it
36
- const cssPath = `${'@' + n.slice(2)}`
37
- cssImports.push(cssPath)
38
-
39
- } else {
40
- code += source.substring(ss, s)
41
- code += isRelative(n)
42
- ? ('@' + n.slice(2))
43
- : externals.has(n) ? n : getModuleUrl(n)
44
- code += source.substring(e, se)
45
- }
46
-
47
- lastIndex = se
48
-
49
- if (n === 'react') {
50
- const statement = source.slice(ss, se)
51
- if (statement.includes('React')) {
52
- hasReactImports = true
53
- }
54
- }
55
-
56
- cssImports.forEach(cssPath => {
57
- code += `\nimport sheet${cssImportIndex} from "${cssPath}" assert { type: "css" };\n`
58
- cssImportIndex++
59
- })
60
- })
61
-
62
- if (cssImports.length) {
63
- code += `const __customStyleSheets = [`
64
- for (let i = 0; i < cssImports.length; i++) {
65
- code += `sheet${i}`
66
- if (i < cssImports.length - 1) {
67
- code += `, `
68
- }
69
- }
70
- code += `];\n`
71
- code += `document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...__customStyleSheets];\n`
72
- }
73
-
74
- code += source.substring(lastIndex)
75
-
76
- if (!hasReactImports) {
77
- code = `import React from 'react';\n${code}`
78
- }
79
-
80
- return code
81
- }
82
-
83
- function createRenderer(createModule_, getModuleUrl) {
84
- let reactRoot
85
-
86
- async function render(files) {
87
- const mod = await createModule_(files, { getModuleUrl })
88
- const React_ = await self.importShim('react')
89
- const ReactDOM_ = await self.importShim('react-dom')
90
-
91
- const _jsx = React_.createElement
92
- const root = document.getElementById('root')
93
- class ErrorBoundary extends React_.Component {
94
- constructor(props) {
95
- super(props)
96
- this.state = { error: null }
97
- }
98
- componentDidCatch(error) {
99
- this.setState({ error })
100
- }
101
- render() {
102
- if (this.state.error) {
103
- return _jsx('div', null, this.state.error.message)
104
- }
105
- return this.props.children
106
- }
107
- }
108
-
109
- const isReact18 = !!ReactDOM_.createRoot
110
- if (isReact18 && !reactRoot) {
111
- reactRoot = ReactDOM_.createRoot(root)
112
- }
113
- const Component = mod.default
114
- const element = _jsx(ErrorBoundary, null, _jsx(Component))
115
- if (isReact18) {
116
- reactRoot.render(element)
117
- } else {
118
- ReactDOM_.render(element, root)
119
- }
120
- }
121
-
122
- return render
123
- }
124
-
125
- function createMainScript({ uid }) {
126
- const code = (
127
- `'use strict';
128
- const _createModule = ${createModule.toString()};
129
- const _createRenderer = ${createRenderer.toString()};
130
-
131
- const getModuleUrl = (m) => window.parent.__devjar__[globalThis.uid].getModuleUrl(m)
132
-
133
- globalThis.uid = ${JSON.stringify(uid)};
134
- globalThis.__render__ = _createRenderer(_createModule, getModuleUrl);
135
- `)
136
- return code
137
- }
138
-
139
- function createEsShimOptionsScript() {
140
- return `window.esmsInitOptions = {
141
- polyfillEnable: ['css-modules', 'json-modules'],
142
- onerror: error => console.log(error),
143
- }`
144
- }
145
-
146
- function useScript() {
147
- return useRef(typeof window !== 'undefined' ? document.createElement('script') : null)
148
- }
149
-
150
- function setScript(scriptRef, scriptContent, { type } = {}) {
151
- const script = scriptRef.current
152
- if (type) script.type = type
153
-
154
- script.src = `data:text/javascript;utf-8,${encodeURIComponent(scriptContent)}`
155
- return script
156
- }
157
-
158
- function useLiveCode({ getModuleUrl }) {
159
- const iframeRef = useRef()
160
- const [error, setError] = useState()
161
- const rerender = useState({})[1]
162
- const appScriptRef = useScript()
163
- const esShimOptionsScript = useScript()
164
- const uid = useId()
165
-
166
- // Let getModuleUrl executed on parent window side since it might involve
167
- // variables that iframe cannot access.
168
- useEffect(() => {
169
- if (!globalThis.__devjar__) {
170
- globalThis.__devjar__ = {};
171
- }
172
- globalThis.__devjar__[uid] = {
173
- getModuleUrl,
174
- }
175
-
176
- return () => {
177
- if (globalThis.__devjar__) {
178
- delete globalThis.__devjar__[uid]
179
- }
180
- }
181
- }, [])
182
-
183
- useEffect(() => {
184
- const iframe = iframeRef.current
185
- if (!iframe || !iframe.contentDocument) return
186
-
187
- const doc = iframe.contentDocument
188
- const div = document.createElement('div')
189
- div.id = 'root'
190
-
191
- const appScriptContent = createMainScript({ uid })
192
- const scriptOptionsContent = createEsShimOptionsScript()
193
-
194
- const esmShimOptionsScript = setScript(esShimOptionsScript, scriptOptionsContent)
195
- const appScript = setScript(appScriptRef, appScriptContent, { type: 'module' })
196
- // const script = scriptRef.current
197
- // script.type = 'module'
198
- // script.id = 'main'
199
- // script.src = `data:text/javascript;utf-8,${encodeURIComponent(scriptContent)}`
200
-
201
- doc.body.appendChild(div)
202
- doc.body.appendChild(esmShimOptionsScript)
203
- doc.body.appendChild(appScript)
204
-
205
- return () => {
206
- if (!iframe || !iframe.contentDocument) return
207
- doc.body.removeChild(div)
208
- doc.body.removeChild(esmShimOptionsScript)
209
- doc.body.removeChild(appScript)
210
- }
211
- }, [])
212
-
213
- const load = useCallback(async (files) => {
214
- if (!esModuleLexerInit) {
215
- await init
216
- esModuleLexerInit = true
217
- }
218
-
219
- if (files) {
220
- // { 'react', 'react-dom' }
221
- const overrideExternals =
222
- new Set(Object.keys(files).filter(name => !isRelative(name) && name !== 'index.js'))
223
-
224
- // Always share react as externals
225
- overrideExternals.add('react')
226
- overrideExternals.add('react-dom')
227
-
228
- try {
229
- /**
230
- * transformedFiles
231
- * {
232
- * 'index.js': '...',
233
- * '@mod1': '...',
234
- * '@mod2': '...',
235
- */
236
- const transformedFiles = Object.keys(files).reduce((res, filename) => {
237
- const key = isRelative(filename) ? ('@' + filename.slice(2)) : filename
238
- if (filename.endsWith('.css')) {
239
- res[key] = files[filename]
240
- } else {
241
- res[key] = transformCode(files[filename], getModuleUrl, overrideExternals)
242
- }
243
- return res
244
- }, {})
245
-
246
- const iframe = iframeRef.current
247
- const script = appScriptRef.current
248
- if (iframe) {
249
- const render = iframe.contentWindow.__render__
250
- if (render) {
251
- render(transformedFiles)
252
- } else {
253
- // if render is not loaded yet, wait until it's loaded
254
- script.onload = () => {
255
- iframe.contentWindow.__render__(transformedFiles)
256
- }
257
- }
258
- }
259
- setError()
260
- } catch (e) {
261
- console.error(e)
262
- setError(e)
263
- }
264
- }
265
- rerender({})
266
- }, [])
267
-
268
- return { ref: iframeRef, error, load }
269
- }
270
-
271
- export {
272
- createModule,
273
- useLiveCode,
274
- }
package/lib/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import React from 'react'
2
-
3
- type LiveCodeHandles = {
4
- load(files: Record<string, string>): void
5
- ref: React.Ref
6
- error?: unknown
7
- }
8
-
9
- type Options = {
10
- getModuleUrl(modulePath: string): string
11
- }
12
-
13
- export function useLiveCode(options: Options): LiveCodeHandles
package/lib/index.mjs DELETED
@@ -1,2 +0,0 @@
1
- export { DevJar } from './render.mjs'
2
- export { useLiveCode } from './core.mjs'
package/lib/module.mjs DELETED
@@ -1,48 +0,0 @@
1
- async function createModule(files, { getModuleUrl }) {
2
- let currentImportMap
3
- let shim
4
-
5
- async function setupImportMap() {
6
- if (shim) return shim
7
- window.esmsInitOptions = {
8
- shimMode: true,
9
- mapOverrides: true,
10
- }
11
- shim = import(/* webpackIgnore: true */ getModuleUrl('es-module-shims'))
12
- await shim
13
- }
14
-
15
- function updateImportMap(imports) {
16
- imports['react'] = getModuleUrl('react')
17
- imports['react-dom'] = getModuleUrl('react-dom')
18
-
19
- const script = document.createElement('script')
20
- script.type = 'importmap-shim'
21
- script.innerHTML = JSON.stringify({ imports })
22
- document.body.appendChild(script)
23
- if (currentImportMap) {
24
- currentImportMap.parentNode.removeChild(currentImportMap)
25
- }
26
- currentImportMap = script
27
- }
28
-
29
-
30
- function createInlinedModule(code, type) {
31
- if (type === 'css') return `data:text/css;utf-8,${encodeURIComponent(code)}`
32
-
33
- return `data:text/javascript;utf-8,${encodeURIComponent(code)}`
34
- }
35
-
36
- await setupImportMap()
37
- const imports = Object.fromEntries(
38
- Object.entries(files).map(([fileName, code]) => [
39
- fileName,
40
- createInlinedModule(code, fileName.endsWith('.css') ? 'css' : 'js'),
41
- ])
42
- )
43
-
44
- updateImportMap(imports)
45
- return self.importShim('index.js')
46
- }
47
-
48
- export { createModule }
package/lib/render.mjs DELETED
@@ -1,21 +0,0 @@
1
- import React, { useEffect, useRef } from 'react'
2
- import { useLiveCode } from './core.mjs'
3
-
4
- const defaultOnError = typeof window !== 'undefined' ? console.error : (() => {})
5
-
6
- export function DevJar({ files, getModuleUrl, onError = defaultOnError, ...props }) {
7
- const onErrorRef = useRef(onError)
8
- const { ref, error, load } = useLiveCode({ getModuleUrl })
9
-
10
- useEffect(() => {
11
- onErrorRef.current(error)
12
- }, [error])
13
-
14
- // load code files and execute them as live code
15
- useEffect(() => {
16
- load(files)
17
- }, [files])
18
-
19
- // Attach the ref to an iframe element for runtime of code execution
20
- return React.createElement('iframe', { ...props, ref })
21
- }