@jbpark/live-editor 1.8.0 → 1.8.1

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/dist/index.d.mts CHANGED
@@ -49,6 +49,7 @@ interface Props$6 {
49
49
  children: React$1.ReactNode;
50
50
  fallback?: (message?: string) => React$1.ReactNode;
51
51
  onError?: (e: Error, info: React$1.ErrorInfo) => void;
52
+ resetKeys?: readonly unknown[];
52
53
  }
53
54
  interface State {
54
55
  hasError: boolean;
@@ -58,6 +59,7 @@ declare class ErrorBoundary extends React$1.Component<Props$6, State> {
58
59
  constructor(props: Props$6);
59
60
  static getDerivedStateFromError(error: Error): State;
60
61
  componentDidCatch(error: Error, errorInfo: React$1.ErrorInfo): void;
62
+ componentDidUpdate(prevProps: Props$6): void;
61
63
  render(): string | number | bigint | boolean | react_jsx_runtime0.JSX.Element | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined> | null | undefined;
62
64
  }
63
65
  //#endregion
package/dist/index.mjs CHANGED
@@ -44,8 +44,12 @@ const useError = () => {
44
44
  //#endregion
45
45
  //#region src/components/context/context.tsx
46
46
  const ContextProvider = ({ children }) => {
47
- const [code, setCode] = useState(DEFAULT_TEMPLATE);
47
+ const [code, setCodeState] = useState(DEFAULT_TEMPLATE);
48
48
  const [error, setError] = useState(null);
49
+ const setCode = useCallback((next) => {
50
+ setError(null);
51
+ setCodeState(next);
52
+ }, []);
49
53
  useEffect(() => {
50
54
  return () => {
51
55
  clearCompilationCache();
@@ -72,7 +76,9 @@ const EMPTY_STRING_ARRAY = [];
72
76
  const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_STRING_ARRAY, styles = EMPTY_STRING_ARRAY, stylesheets = EMPTY_STRING_ARRAY, autoHeight = false, syncStyle = false, children, onLoaded, ...props }) => {
73
77
  const iframeRef = useRef(null);
74
78
  const [mountNode, setMountNode] = useState(null);
75
- const scriptsLoadedRef = useRef(false);
79
+ const loadedScriptsRef = useRef(/* @__PURE__ */ new Set());
80
+ const prevStyleCountRef = useRef(0);
81
+ const prevStylesheetCountRef = useRef(0);
76
82
  const shouldAutoHeight = autoHeight && style.height == null;
77
83
  const styleManagerRef = useRef({
78
84
  copiedLinks: /* @__PURE__ */ new Set(),
@@ -129,9 +135,10 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
129
135
  }
130
136
  setMountNode(node);
131
137
  applyStyle();
132
- if (scripts.length && !scriptsLoadedRef.current) {
133
- scriptsLoadedRef.current = true;
134
- Promise.all(scripts.map(getCachedScriptBlob)).then((blobUrls) => {
138
+ const pendingScripts = scripts.filter((src) => !loadedScriptsRef.current.has(src));
139
+ if (pendingScripts.length) {
140
+ pendingScripts.forEach((src) => loadedScriptsRef.current.add(src));
141
+ Promise.all(pendingScripts.map(getCachedScriptBlob)).then((blobUrls) => {
135
142
  if (!doc.head) return;
136
143
  const fragment = doc.createDocumentFragment();
137
144
  blobUrls.forEach((blobUrl) => {
@@ -184,6 +191,8 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
184
191
  }
185
192
  if (styleEl.textContent !== css) styleEl.textContent = css;
186
193
  });
194
+ for (let index = styles.length; index < prevStyleCountRef.current; index++) doc.getElementById(`injected-style-${index}`)?.remove();
195
+ prevStyleCountRef.current = styles.length;
187
196
  stylesheets.forEach((href, index) => {
188
197
  const linkId = `injected-stylesheet-${index}`;
189
198
  let linkEl = doc.getElementById(linkId);
@@ -195,6 +204,8 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
195
204
  }
196
205
  if (linkEl.href !== href) linkEl.href = href;
197
206
  });
207
+ for (let index = stylesheets.length; index < prevStylesheetCountRef.current; index++) doc.getElementById(`injected-stylesheet-${index}`)?.remove();
208
+ prevStylesheetCountRef.current = stylesheets.length;
198
209
  }, [styles, stylesheets]);
199
210
  useEffect(() => {
200
211
  if (!shouldAutoHeight || !mountNode || !iframeRef.current) return;
@@ -295,41 +306,6 @@ const Shadow = ({ children }) => {
295
306
  renderTargetRef.current = target;
296
307
  setRenderTarget(target);
297
308
  }
298
- const onClick = (e) => {
299
- const eventInit = {
300
- bubbles: true,
301
- cancelable: true,
302
- composed: true
303
- };
304
- let newEvent;
305
- if (e instanceof MouseEvent) newEvent = new MouseEvent(e.type, {
306
- ...eventInit,
307
- clientX: e.clientX,
308
- clientY: e.clientY,
309
- button: e.button
310
- });
311
- else if (e instanceof PointerEvent) newEvent = new PointerEvent(e.type, {
312
- ...eventInit,
313
- clientX: e.clientX,
314
- clientY: e.clientY,
315
- pointerId: e.pointerId
316
- });
317
- else newEvent = new Event(e.type, eventInit);
318
- hostRef.current?.dispatchEvent(newEvent);
319
- };
320
- const eventTypes = [
321
- "click",
322
- "pointerdown",
323
- "pointerup"
324
- ];
325
- eventTypes.forEach((type) => {
326
- target.addEventListener(type, onClick, true);
327
- });
328
- return () => {
329
- eventTypes.forEach((type) => {
330
- target?.removeEventListener(type, onClick, true);
331
- });
332
- };
333
309
  }, []);
334
310
  useLayoutEffect(() => {
335
311
  if (renderTargetRef.current && !renderTarget) setRenderTarget(renderTargetRef.current);
@@ -402,7 +378,16 @@ const Renderer = ({ preview, headers, modules, frame, provider }) => {
402
378
  ...baseModules,
403
379
  ...modules
404
380
  }), [modules]);
405
- const module = useMemo(() => compile(preview, memoizedModules), [preview, memoizedModules]);
381
+ const module = useMemo(() => {
382
+ try {
383
+ return compile(preview, memoizedModules);
384
+ } catch (e) {
385
+ return {
386
+ exports: {},
387
+ error: e instanceof Error ? e.message : "Module transformation error"
388
+ };
389
+ }
390
+ }, [preview, memoizedModules]);
406
391
  const renderProvider = (component) => {
407
392
  return provider ? provider(component) : component;
408
393
  };
@@ -12935,6 +12920,15 @@ var ErrorBoundary = class extends React.Component {
12935
12920
  console.error("🚨 [Boundary] Rendering error:", error, errorInfo);
12936
12921
  this.props.onError?.(error, errorInfo);
12937
12922
  }
12923
+ componentDidUpdate(prevProps) {
12924
+ if (!this.state.hasError) return;
12925
+ const prevKeys = prevProps.resetKeys ?? [];
12926
+ const nextKeys = this.props.resetKeys ?? [];
12927
+ if (prevKeys.length !== nextKeys.length || nextKeys.some((key, i) => key !== prevKeys[i])) this.setState({
12928
+ hasError: false,
12929
+ error: void 0
12930
+ });
12931
+ }
12938
12932
  render() {
12939
12933
  if (this.state.hasError) return this.props.fallback ? /* @__PURE__ */ jsx(Fragment, { children: this.props.fallback(this.state.error?.message) }) : /* @__PURE__ */ jsx(Error$2, {
12940
12934
  message: this.state.error?.message,
@@ -13043,9 +13037,10 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13043
13037
  ...baseModules,
13044
13038
  ...modules
13045
13039
  };
13040
+ const effectiveCode = _code || code;
13046
13041
  let module = null;
13047
- if (_code || code) try {
13048
- module = compile(_code || code, mergedModules);
13042
+ if (effectiveCode) try {
13043
+ module = compile(effectiveCode, mergedModules);
13049
13044
  } catch (e) {
13050
13045
  module = {
13051
13046
  exports: {},
@@ -13070,6 +13065,7 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13070
13065
  children: /* @__PURE__ */ jsx(Frame, {
13071
13066
  ...frame,
13072
13067
  children: (container) => /* @__PURE__ */ jsx(Error$1.Boundary, {
13068
+ resetKeys: [effectiveCode],
13073
13069
  onError: (e) => setError(e.message),
13074
13070
  children: renderProvider(/* @__PURE__ */ jsx(Error$1.Guard, {
13075
13071
  onError: (e) => setError(e.message),
@@ -13088,6 +13084,7 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13088
13084
  containerType: "inline-size"
13089
13085
  },
13090
13086
  children: /* @__PURE__ */ jsx(Error$1.Boundary, {
13087
+ resetKeys: [effectiveCode],
13091
13088
  onError: (e) => setError(e.message),
13092
13089
  children: renderProvider(/* @__PURE__ */ jsx(Error$1.Guard, {
13093
13090
  onError: (e) => setError(e.message),
@@ -13102,12 +13099,23 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13102
13099
  const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, provider, ...restProps }) => {
13103
13100
  const [runtimeError, setRuntimeError] = useState(null);
13104
13101
  const [dynamicCSS, setDynamicCSS] = useState("");
13102
+ const [prevCode, setPrevCode] = useState(code);
13103
+ if (code !== prevCode) {
13104
+ setPrevCode(code);
13105
+ setRuntimeError(null);
13106
+ }
13105
13107
  const renderProvider = (component) => {
13106
13108
  return provider ? provider(component) : component;
13107
13109
  };
13108
13110
  useEffect(() => {
13109
13111
  if (!code || !dynamicTailwind) return;
13110
- generateTailwindCSS(code).then(setDynamicCSS);
13112
+ let cancelled = false;
13113
+ generateTailwindCSS(code).then((css) => {
13114
+ if (!cancelled) setDynamicCSS(css);
13115
+ });
13116
+ return () => {
13117
+ cancelled = true;
13118
+ };
13111
13119
  }, [code, dynamicTailwind]);
13112
13120
  if (runtimeError) return /* @__PURE__ */ jsx(Error$1, {
13113
13121
  title: "Runtime Error",
@@ -13116,10 +13124,18 @@ const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, prov
13116
13124
  onReset: () => setRuntimeError(null)
13117
13125
  });
13118
13126
  if (code) {
13119
- const module = compile(code, {
13120
- ...baseModules,
13121
- ...modules
13122
- });
13127
+ let module;
13128
+ try {
13129
+ module = compile(code, {
13130
+ ...baseModules,
13131
+ ...modules
13132
+ });
13133
+ } catch (e) {
13134
+ module = {
13135
+ exports: {},
13136
+ error: e instanceof Error ? e.message : "Module transformation error"
13137
+ };
13138
+ }
13123
13139
  if (module.error) return /* @__PURE__ */ jsx(Error$1, {
13124
13140
  title: "Compile Error",
13125
13141
  message: module.error,
@@ -13131,13 +13147,14 @@ const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, prov
13131
13147
  className: "mx-5 mt-25"
13132
13148
  });
13133
13149
  return /* @__PURE__ */ jsx(Error$1.Boundary, {
13150
+ resetKeys: [code],
13134
13151
  fallback: (message) => /* @__PURE__ */ jsx(Error$1, {
13135
13152
  title: "Rendering Error",
13136
13153
  message
13137
13154
  }),
13138
13155
  children: renderProvider(/* @__PURE__ */ jsxs(Error$1.Guard, {
13139
13156
  onError: (e) => setRuntimeError(e.message),
13140
- children: [/* @__PURE__ */ jsx(Component, { ...props }), dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })]
13157
+ children: [/* @__PURE__ */ jsx(Component, { ...props }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })]
13141
13158
  }))
13142
13159
  });
13143
13160
  }