@tanstack/devtools-utils 0.1.0 → 0.2.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.
@@ -5,15 +5,14 @@ function createReactPanel(CoreClass) {
5
5
  const devToolRef = useRef(null);
6
6
  const devtools = useRef(null);
7
7
  useEffect(() => {
8
- if (devtools.current) return;
9
- devtools.current = new CoreClass();
8
+ const instance = new CoreClass();
9
+ devtools.current = instance;
10
10
  if (devToolRef.current) {
11
- devtools.current.mount(devToolRef.current, props?.theme ?? "dark");
11
+ instance.mount(devToolRef.current, props?.theme ?? "dark");
12
12
  }
13
13
  return () => {
14
- if (devToolRef.current) {
15
- devtools.current?.unmount();
16
- }
14
+ instance.unmount();
15
+ devtools.current = null;
17
16
  };
18
17
  }, [props?.theme]);
19
18
  return /* @__PURE__ */ jsx("div", { style: { height: "100%" }, ref: devToolRef });
@@ -1 +1 @@
1
- {"version":3,"file":"panel.js","sources":["../../../src/react/panel.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nexport interface DevtoolsPanelProps {\n theme?: 'light' | 'dark'\n}\n\n/**\n * Creates a React component that dynamically imports and mounts a devtools panel. SSR friendly.\n * @param devtoolsPackageName The name of the devtools package to be imported, e.g., '@tanstack/devtools-react'\n * @param importName The name of the export to be imported from the devtools package (e.g., 'default' or 'DevtoolsCore')\n * @returns A React component that mounts the devtools\n * @example\n * ```tsx\n * // if the export is default\n * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react')\n * ```\n *\n * @example\n * ```tsx\n * // if the export is named differently\n * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'DevtoolsCore')\n * ```\n */\nexport function createReactPanel<\n TComponentProps extends DevtoolsPanelProps | undefined,\n TCoreDevtoolsClass extends {\n mount: (el: HTMLElement, theme: 'light' | 'dark') => void\n unmount: () => void\n },\n>(CoreClass: new () => TCoreDevtoolsClass) {\n function Panel(props: TComponentProps) {\n const devToolRef = useRef<HTMLDivElement>(null)\n const devtools = useRef<TCoreDevtoolsClass | null>(null)\n useEffect(() => {\n if (devtools.current) return\n\n devtools.current = new CoreClass()\n\n if (devToolRef.current) {\n devtools.current.mount(devToolRef.current, props?.theme ?? 'dark')\n }\n\n return () => {\n if (devToolRef.current) {\n devtools.current?.unmount()\n }\n }\n }, [props?.theme])\n\n return <div style={{ height: '100%' }} ref={devToolRef} />\n }\n\n function NoOpPanel(_props: TComponentProps) {\n return <></>\n }\n return [Panel, NoOpPanel] as const\n}\n"],"names":[],"mappings":";;AAuBO,SAAS,iBAMd,WAAyC;AACzC,WAAS,MAAM,OAAwB;AACrC,UAAM,aAAa,OAAuB,IAAI;AAC9C,UAAM,WAAW,OAAkC,IAAI;AACvD,cAAU,MAAM;AACd,UAAI,SAAS,QAAS;AAEtB,eAAS,UAAU,IAAI,UAAA;AAEvB,UAAI,WAAW,SAAS;AACtB,iBAAS,QAAQ,MAAM,WAAW,SAAS,OAAO,SAAS,MAAM;AAAA,MACnE;AAEA,aAAO,MAAM;AACX,YAAI,WAAW,SAAS;AACtB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF;AAAA,IACF,GAAG,CAAC,OAAO,KAAK,CAAC;AAEjB,WAAO,oBAAC,SAAI,OAAO,EAAE,QAAQ,OAAA,GAAU,KAAK,YAAY;AAAA,EAC1D;AAEA,WAAS,UAAU,QAAyB;AAC1C,WAAO,oBAAA,UAAA,EAAE;AAAA,EACX;AACA,SAAO,CAAC,OAAO,SAAS;AAC1B;"}
1
+ {"version":3,"file":"panel.js","sources":["../../../src/react/panel.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nexport interface DevtoolsPanelProps {\n theme?: 'light' | 'dark'\n}\n\n/**\n * Creates a React component that dynamically imports and mounts a devtools panel. SSR friendly.\n * @param devtoolsPackageName The name of the devtools package to be imported, e.g., '@tanstack/devtools-react'\n * @param importName The name of the export to be imported from the devtools package (e.g., 'default' or 'DevtoolsCore')\n * @returns A React component that mounts the devtools\n * @example\n * ```tsx\n * // if the export is default\n * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react')\n * ```\n *\n * @example\n * ```tsx\n * // if the export is named differently\n * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'DevtoolsCore')\n * ```\n */\nexport function createReactPanel<\n TComponentProps extends DevtoolsPanelProps | undefined,\n TCoreDevtoolsClass extends {\n mount: (el: HTMLElement, theme: 'light' | 'dark') => void\n unmount: () => void\n },\n>(CoreClass: new () => TCoreDevtoolsClass) {\n function Panel(props: TComponentProps) {\n const devToolRef = useRef<HTMLDivElement>(null)\n const devtools = useRef<TCoreDevtoolsClass | null>(null)\n useEffect(() => {\n const instance = new CoreClass()\n devtools.current = instance\n\n if (devToolRef.current) {\n instance.mount(devToolRef.current, props?.theme ?? 'dark')\n }\n\n return () => {\n instance.unmount()\n devtools.current = null\n }\n }, [props?.theme])\n\n return <div style={{ height: '100%' }} ref={devToolRef} />\n }\n\n function NoOpPanel(_props: TComponentProps) {\n return <></>\n }\n return [Panel, NoOpPanel] as const\n}\n"],"names":[],"mappings":";;AAuBO,SAAS,iBAMd,WAAyC;AACzC,WAAS,MAAM,OAAwB;AACrC,UAAM,aAAa,OAAuB,IAAI;AAC9C,UAAM,WAAW,OAAkC,IAAI;AACvD,cAAU,MAAM;AACd,YAAM,WAAW,IAAI,UAAA;AACrB,eAAS,UAAU;AAEnB,UAAI,WAAW,SAAS;AACtB,iBAAS,MAAM,WAAW,SAAS,OAAO,SAAS,MAAM;AAAA,MAC3D;AAEA,aAAO,MAAM;AACX,iBAAS,QAAA;AACT,iBAAS,UAAU;AAAA,MACrB;AAAA,IACF,GAAG,CAAC,OAAO,KAAK,CAAC;AAEjB,WAAO,oBAAC,SAAI,OAAO,EAAE,QAAQ,OAAA,GAAU,KAAK,YAAY;AAAA,EAC1D;AAEA,WAAS,UAAU,QAAyB;AAC1C,WAAO,oBAAA,UAAA,EAAE;AAAA,EACX;AACA,SAAO,CAAC,OAAO,SAAS;AAC1B;"}
@@ -6,12 +6,15 @@ var _tmpl$ = /* @__PURE__ */ template(`<div>`);
6
6
  function constructCoreClass(Component) {
7
7
  class DevtoolsCore {
8
8
  #isMounted = false;
9
+ #isMounting = false;
10
+ #mountCb = null;
9
11
  #dispose;
10
12
  #Component;
11
13
  #ThemeProvider;
12
14
  constructor() {
13
15
  }
14
16
  async mount(el, theme) {
17
+ this.#isMounting = true;
15
18
  const {
16
19
  lazy
17
20
  } = await import('solid-js');
@@ -46,12 +49,24 @@ function constructCoreClass(Component) {
46
49
  });
47
50
  }, mountTo);
48
51
  this.#isMounted = true;
52
+ this.#isMounting = false;
49
53
  this.#dispose = dispose;
54
+ if (this.#mountCb) {
55
+ this.#mountCb();
56
+ this.#mountCb = null;
57
+ }
50
58
  }
51
59
  unmount() {
52
- if (!this.#isMounted) {
60
+ if (!this.#isMounted && !this.#isMounting) {
53
61
  throw new Error("Devtools is not mounted");
54
62
  }
63
+ if (this.#isMounting) {
64
+ this.#mountCb = () => {
65
+ this.#dispose?.();
66
+ this.#isMounted = false;
67
+ };
68
+ return;
69
+ }
55
70
  this.#dispose?.();
56
71
  this.#isMounted = false;
57
72
  }
@@ -15,6 +15,8 @@ import { JSX } from 'solid-js';
15
15
  declare function constructCoreClass(Component: () => JSX.Element): readonly [{
16
16
  new (): {
17
17
  "__#private@#isMounted": boolean;
18
+ "__#private@#isMounting": boolean;
19
+ "__#private@#mountCb": (() => void) | null;
18
20
  "__#private@#dispose"?: () => void;
19
21
  "__#private@#Component": any;
20
22
  "__#private@#ThemeProvider": any;
@@ -26,6 +28,8 @@ declare function constructCoreClass(Component: () => JSX.Element): readonly [{
26
28
  mount<T extends HTMLElement>(_el: T, _theme: "light" | "dark"): Promise<void>;
27
29
  unmount(): void;
28
30
  "__#private@#isMounted": boolean;
31
+ "__#private@#isMounting": boolean;
32
+ "__#private@#mountCb": (() => void) | null;
29
33
  "__#private@#dispose"?: () => void;
30
34
  "__#private@#Component": any;
31
35
  "__#private@#ThemeProvider": any;
@@ -6,12 +6,15 @@ var _tmpl$ = /* @__PURE__ */ template(`<div>`);
6
6
  function constructCoreClass(Component) {
7
7
  class DevtoolsCore {
8
8
  #isMounted = false;
9
+ #isMounting = false;
10
+ #mountCb = null;
9
11
  #dispose;
10
12
  #Component;
11
13
  #ThemeProvider;
12
14
  constructor() {
13
15
  }
14
16
  async mount(el, theme) {
17
+ this.#isMounting = true;
15
18
  const {
16
19
  lazy
17
20
  } = await import('solid-js');
@@ -46,12 +49,24 @@ function constructCoreClass(Component) {
46
49
  });
47
50
  }, mountTo);
48
51
  this.#isMounted = true;
52
+ this.#isMounting = false;
49
53
  this.#dispose = dispose;
54
+ if (this.#mountCb) {
55
+ this.#mountCb();
56
+ this.#mountCb = null;
57
+ }
50
58
  }
51
59
  unmount() {
52
- if (!this.#isMounted) {
60
+ if (!this.#isMounted && !this.#isMounting) {
53
61
  throw new Error("Devtools is not mounted");
54
62
  }
63
+ if (this.#isMounting) {
64
+ this.#mountCb = () => {
65
+ this.#dispose?.();
66
+ this.#isMounted = false;
67
+ };
68
+ return;
69
+ }
55
70
  this.#dispose?.();
56
71
  this.#isMounted = false;
57
72
  }
@@ -6,12 +6,15 @@ var _tmpl$ = ['<div style="', '">', "</div>"];
6
6
  function constructCoreClass(Component) {
7
7
  class DevtoolsCore {
8
8
  #isMounted = false;
9
+ #isMounting = false;
10
+ #mountCb = null;
9
11
  #dispose;
10
12
  #Component;
11
13
  #ThemeProvider;
12
14
  constructor() {
13
15
  }
14
16
  async mount(el, theme) {
17
+ this.#isMounting = true;
15
18
  const {
16
19
  lazy
17
20
  } = await import('solid-js');
@@ -43,12 +46,24 @@ function constructCoreClass(Component) {
43
46
  });
44
47
  }, mountTo);
45
48
  this.#isMounted = true;
49
+ this.#isMounting = false;
46
50
  this.#dispose = dispose;
51
+ if (this.#mountCb) {
52
+ this.#mountCb();
53
+ this.#mountCb = null;
54
+ }
47
55
  }
48
56
  unmount() {
49
- if (!this.#isMounted) {
57
+ if (!this.#isMounted && !this.#isMounting) {
50
58
  throw new Error("Devtools is not mounted");
51
59
  }
60
+ if (this.#isMounting) {
61
+ this.#mountCb = () => {
62
+ this.#dispose?.();
63
+ this.#isMounted = false;
64
+ };
65
+ return;
66
+ }
52
67
  this.#dispose?.();
53
68
  this.#isMounted = false;
54
69
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@tanstack/devtools-utils",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "TanStack Devtools utilities for creating your own devtools.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/TanStack/devtools.git",
10
- "directory": "packages/devtools"
9
+ "url": "git+https://github.com/TanStack/devtools.git",
10
+ "directory": "packages/devtools-utils"
11
11
  },
12
12
  "homepage": "https://tanstack.com/devtools",
13
13
  "funding": {
@@ -32,18 +32,16 @@ export function createReactPanel<
32
32
  const devToolRef = useRef<HTMLDivElement>(null)
33
33
  const devtools = useRef<TCoreDevtoolsClass | null>(null)
34
34
  useEffect(() => {
35
- if (devtools.current) return
36
-
37
- devtools.current = new CoreClass()
35
+ const instance = new CoreClass()
36
+ devtools.current = instance
38
37
 
39
38
  if (devToolRef.current) {
40
- devtools.current.mount(devToolRef.current, props?.theme ?? 'dark')
39
+ instance.mount(devToolRef.current, props?.theme ?? 'dark')
41
40
  }
42
41
 
43
42
  return () => {
44
- if (devToolRef.current) {
45
- devtools.current?.unmount()
46
- }
43
+ instance.unmount()
44
+ devtools.current = null
47
45
  }
48
46
  }, [props?.theme])
49
47
 
@@ -14,6 +14,8 @@ import type { JSX } from 'solid-js'
14
14
  export function constructCoreClass(Component: () => JSX.Element) {
15
15
  class DevtoolsCore {
16
16
  #isMounted = false
17
+ #isMounting = false
18
+ #mountCb: (() => void) | null = null
17
19
  #dispose?: () => void
18
20
  #Component: any
19
21
  #ThemeProvider: any
@@ -21,6 +23,7 @@ export function constructCoreClass(Component: () => JSX.Element) {
21
23
  constructor() {}
22
24
 
23
25
  async mount<T extends HTMLElement>(el: T, theme: 'light' | 'dark') {
26
+ this.#isMounting = true
24
27
  const { lazy } = await import('solid-js')
25
28
  const { render, Portal } = await import('solid-js/web')
26
29
  if (this.#isMounted) {
@@ -49,13 +52,25 @@ export function constructCoreClass(Component: () => JSX.Element) {
49
52
  )
50
53
  }, mountTo)
51
54
  this.#isMounted = true
55
+ this.#isMounting = false
52
56
  this.#dispose = dispose
57
+ if (this.#mountCb) {
58
+ this.#mountCb()
59
+ this.#mountCb = null
60
+ }
53
61
  }
54
62
 
55
63
  unmount() {
56
- if (!this.#isMounted) {
64
+ if (!this.#isMounted && !this.#isMounting) {
57
65
  throw new Error('Devtools is not mounted')
58
66
  }
67
+ if (this.#isMounting) {
68
+ this.#mountCb = () => {
69
+ this.#dispose?.()
70
+ this.#isMounted = false
71
+ }
72
+ return
73
+ }
59
74
  this.#dispose?.()
60
75
  this.#isMounted = false
61
76
  }