Skip to content

Commit

Permalink
devtools improvements - is now injected in kaioken module instead of …
Browse files Browse the repository at this point in the history
…user code, handles SSR better
  • Loading branch information
LankyMoose committed Apr 13, 2024
1 parent 370e47d commit fd8dfdb
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 78 deletions.
9 changes: 3 additions & 6 deletions packages/devtools-client/src/components/SelectedNodeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ export function SelectedNodeView() {
{selectedNode.hooks && (
<div className="text-sm">
{selectedNode.hooks.map((hookData) => {
const data = { ...hookData }
const name = data.name
delete data.name
const { name, debug, ...rest } = hookData
const data = debug ? debug() : rest
return (
<div>
<b>{name || "anonymous hook"}</b>
Expand All @@ -56,9 +55,7 @@ export function SelectedNodeView() {
<div className="flex gap-2 mb-2">
<b className="p-2">{key}:</b>{" "}
<pre className="p-2 bg-neutral-800">
{Array.isArray(value)
? JSON.stringify(value)
: JSON.stringify(value, null, 2)}
{JSON.stringify(value, null, 2)}
</pre>
</div>
)
Expand Down
6 changes: 5 additions & 1 deletion packages/devtools-client/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { AppContext, createStore } from "kaioken"
import { isDevtoolsApp } from "./utils"

export const kaiokenGlobal = window.opener.__kaioken as typeof window.__kaioken
export const kaiokenGlobal =
"window" in globalThis
? (window.opener.__kaioken as typeof window.__kaioken)
: undefined

const initialApps = (kaiokenGlobal?.apps ?? []).filter(
(app) => !isDevtoolsApp(app)
)
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-client/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppContext } from "kaioken"
import type { AppContext } from "kaioken"

export function isDevtoolsApp(app: AppContext) {
return app.name === "kaioken.devtools"
Expand Down
37 changes: 22 additions & 15 deletions packages/devtools-host/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { useEffect as __devtoolsUseEffect } from "kaioken"
import { __useDevtoolsStore } from "./store"
import { useEffect, useRef } from "kaioken"
import { useDevtoolsStore } from "./store"

function __devtoolsHandleDragOver(e: DragEvent) {
const dragging = __useDevtoolsStore.getState().dragging
function handleDragOver(e: DragEvent) {
const dragging = useDevtoolsStore.getState().dragging
if (!dragging) return
e.preventDefault()
e.stopPropagation()
e.dataTransfer && (e.dataTransfer.dropEffect = "move")
}

export default function __DevtoolsApp() {
export default function App() {
const bgRef = useRef<HTMLDivElement>(null)
const {
value: { popupWindow, dragging },
setPopupWindow,
setCorner,
setDragging,
} = __useDevtoolsStore()
} = useDevtoolsStore()

__devtoolsUseEffect(() => {
document.body.addEventListener("dragover", __devtoolsHandleDragOver)
useEffect(() => {
document.body.addEventListener("dragover", handleDragOver)
return () => {
document.body.removeEventListener("dragover", __devtoolsHandleDragOver)
document.body.removeEventListener("dragover", handleDragOver)
}
}, [])

Expand All @@ -41,33 +42,39 @@ export default function __DevtoolsApp() {
}

function handleDragStart() {
__useDevtoolsStore.setState((prev) => ({
...prev,
dragging: true,
}))
setDragging(true)
}

function handleDrag(e: DragEvent) {
if (!bgRef.current?.isConnected) return
e.preventDefault()
e.stopPropagation()
if (!useDevtoolsStore.getState().dragging) return

let isLeft = true,
isTop = true
if (e.pageX > window.innerWidth / 2) isLeft = false
if (e.pageY > window.innerHeight / 2) isTop = false
if (e.offsetX > window.innerWidth / 2 + window.scrollX) isLeft = false
if (e.offsetY > window.innerHeight / 2 + window.scrollY) isTop = false

const corner = isTop ? (isLeft ? "tl" : "tr") : isLeft ? "bl" : "br"
setCorner(corner)
}

function handleDragEnd(e: DragEvent) {
if (!bgRef.current?.isConnected) return
e.preventDefault()
e.stopPropagation()
setDragging(false)
}

return (
<>
{dragging ? (
<div
ref={bgRef}
style="position:fixed;top:0;left:0;width:100vw;height:100vh;"
/>
) : null}
<button
draggable
onclick={handleOpen}
Expand Down
78 changes: 42 additions & 36 deletions packages/devtools-host/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
import { mount as __devtoolsMount } from "kaioken"
import __DevtoolsApp from "./App"
import { type AnchorCorner, __useDevtoolsStore } from "./store"
import App from "./App"
import { type AnchorCorner, useDevtoolsStore } from "./store"
;(() => {
if ("window" in globalThis) {
//@ts-ignore
if (window.__kaiokenDevtools) return

function __devtoolsGetCornerStyle(corner: AnchorCorner) {
switch (corner) {
case "br":
return "bottom:0;right:0;"
case "bl":
return "bottom:0;left:0;"
case "tl":
return "top:0;left:0;"
case "tr":
return "top:0;right:0;"
}
}

if ("window" in globalThis) {
const __devtoolsRoot = Object.assign(document.createElement("div"), {
id: "devtools-root",
})
const style = __devtoolsGetCornerStyle(__useDevtoolsStore.getState().corner)
__devtoolsRoot.setAttribute("style", "position:fixed;" + style)
document.body.appendChild(__devtoolsRoot)

__useDevtoolsStore.subscribe((state) => {
const style = __devtoolsGetCornerStyle(state.corner)
__devtoolsRoot.setAttribute("style", "position:fixed;" + style)
})

__devtoolsMount(__DevtoolsApp, {
root: __devtoolsRoot,
name: "kaioken.devtools",
})
window.__kaioken?.on("mount", () => {
//@ts-ignore
if (window.__kaiokenDevtools) return
const root = Object.assign(document.createElement("div"), {
id: "devtools-root",
})
const style = getCornerStyle(useDevtoolsStore.getState().corner)
root.setAttribute("style", "position:fixed;" + style)
document.body.appendChild(root)

window.onbeforeunload = () => {
const { popupWindow } = __useDevtoolsStore.getState()
popupWindow?.close()
useDevtoolsStore.subscribe((state) => {
const style = getCornerStyle(state.corner)
root.setAttribute("style", "position:fixed;" + style)
})
//@ts-ignore
window.__kaiokenDevtools = __devtoolsMount(App, {
root,
name: "kaioken.devtools",
})
window.addEventListener("beforeunload", () => {
const { popupWindow } = useDevtoolsStore.getState()
popupWindow?.close()
})
})
function getCornerStyle(corner: AnchorCorner) {
switch (corner) {
case "br":
return "bottom:0;right:0;"
case "bl":
return "bottom:0;left:0;"
case "tl":
return "top:0;left:0;"
case "tr":
return "top:0;right:0;"
}
}
}
}
})()
8 changes: 5 additions & 3 deletions packages/devtools-host/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import fs from "node:fs"
export const options: BuildOptions = {
entryPoints: ["src/index.ts"],
jsx: "transform",
jsxFactory: "devtoolsKaioken.createElement",
jsxFragment: "devtoolsKaioken.fragment",
jsxFactory: "createElement",
jsxFragment: "fragment",
bundle: true,
platform: "browser",
target: "es2020",
Expand All @@ -20,7 +20,9 @@ export function writeFile(content: string) {
fs.mkdirSync("dist")
fs.writeFileSync(
"dist/index.js",
`export default \`import * as devtoolsKaioken from 'kaioken';\n${content.replace(/[`\\$]/g, "\\$&")}\``,
`export default \`
import {createElement,fragment} from "./dist/index.js"
${content.replace(/[`\\$]/g, "\\$&").replaceAll(`from "kaioken"`, `from "./dist/index.js"`)}\``,
{
encoding: "utf-8",
}
Expand Down
15 changes: 10 additions & 5 deletions packages/devtools-host/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { createStore as __devtoolsCreateStore } from "kaioken"
import { createStore } from "kaioken"

export type AnchorCorner = "br" | "bl" | "tl" | "tr"

const __devtoolsInitialCorner =
localStorage.getItem("kaioken.devtools.anchorCorner") ?? "br"
let initialCorner = "br"
if ("window" in globalThis) {
const corner = localStorage.getItem("kaioken.devtools.anchorCorner")
if (corner) {
initialCorner = corner as AnchorCorner
}
}

export const __useDevtoolsStore = __devtoolsCreateStore(
export const useDevtoolsStore = createStore(
{
popupWindow: null as Window | null,
corner: __devtoolsInitialCorner as AnchorCorner,
corner: initialCorner as AnchorCorner,
dragging: false,
},
(set, get) => ({
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-host/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
//"emitDeclarationOnly": true,
"declaration": true,
Expand Down
24 changes: 14 additions & 10 deletions packages/vite-plugin-kaioken/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { ESBuildOptions, ModuleNode, Plugin, UserConfig } from "vite"
import devtoolsLinkScript from "kaioken-devtools-host"
import devtoolsUiScript from "kaioken-devtools-client"
import { createRequire } from "node:module"

// console.log("devtoolsUiServer", devtoolsUiServer)
const require = createRequire(import.meta.url)

const defaultEsBuildOptions: ESBuildOptions = {
jsxInject: `import * as kaioken from "kaioken"`,
Expand All @@ -24,7 +25,12 @@ export default function (
): Plugin {
let isProduction = false
let isBuild = false
let devtoolsModuleId: string | null = null

const kaiokenModuleId = require
.resolve("kaioken", {
paths: [process.cwd()],
})
.replace(/\\/g, "/")

return {
name: "vite-plugin-kaioken",
Expand All @@ -41,7 +47,7 @@ export default function (
isProduction = config.isProduction
isBuild = config.command === "build"
},
async configureServer(server) {
configureServer(server) {
if (isProduction || isBuild || !opts.devtools) return
server.middlewares.use("/__devtools__", (_, res) => {
res.end(devtoolsUiScript)
Expand Down Expand Up @@ -70,14 +76,12 @@ export default function (
},
transform(code, id) {
if (isProduction || isBuild) return
if (!/\.(tsx|jsx)$/.test(id)) return { code }
if (
opts.devtools &&
(devtoolsModuleId === null || devtoolsModuleId === id)
) {
code = devtoolsLinkScript + code
devtoolsModuleId = id
if (opts.devtools && id === kaiokenModuleId) {
code = code + devtoolsLinkScript
return { code }
}

if (!/\.(tsx|jsx)$/.test(id)) return { code }
const ast = this.parse(code)
try {
const componentNames = findExportedComponentNames(ast.body as AstNode[])
Expand Down

0 comments on commit fd8dfdb

Please sign in to comment.