From 2092c9518a8462693d71bc6be4f304c6b0f3270b Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 00:43:33 +0200 Subject: [PATCH 01/13] add babel plugin for wrapping solid stores + restructure the babel-plugin package --- packages/babel-plugin/src/index.ts | 103 ++++++------------- packages/babel-plugin/src/jsxLocation.ts | 61 +++++++++++ packages/babel-plugin/src/utils.ts | 10 +- packages/babel-plugin/src/wrapStores.ts | 125 +++++++++++++++++++++++ packages/logger/src/index.ts | 36 +++++++ playgrounds/sandbox/package.json | 1 + playgrounds/sandbox/src/App.tsx | 1 - playgrounds/sandbox/src/Todos.tsx | 32 +++--- playgrounds/sandbox/src/index.tsx | 12 +++ playgrounds/sandbox/vite.config.ts | 1 - pnpm-lock.yaml | 6 ++ 11 files changed, 292 insertions(+), 96 deletions(-) create mode 100644 packages/babel-plugin/src/jsxLocation.ts create mode 100644 packages/babel-plugin/src/wrapStores.ts diff --git a/packages/babel-plugin/src/index.ts b/packages/babel-plugin/src/index.ts index 28004232..afdf19f5 100644 --- a/packages/babel-plugin/src/index.ts +++ b/packages/babel-plugin/src/index.ts @@ -1,76 +1,18 @@ -import { transformAsync } from "@babel/core" -import { Visitor } from "@babel/traverse" +import { PluginItem, transformAsync } from "@babel/core" import { PluginOption } from "vite" -import * as t from "@babel/types" -import { getLocationAttribute, isFileJSX, isLowercase } from "./utils" -import { LOCATION_ATTRIBUTE_NAME, WINDOW_PROJECTPATH_PROPERTY } from "@shared/variables" -import { relative } from "path" +import { getFileExtension } from "./utils" +import jsxLocationPlugin from "./jsxLocation" +import wrapStoresPlugin from "./wrapStores" -// This is the entry point for babel. -export default (): { - name: string - visitor: Visitor -} => { - return { - name: "@solid-devtools/babel-plugin", - visitor: { - Program(path, state) { - const { cwd, filename } = state as { cwd: unknown; filename: unknown } - if (typeof cwd !== "string" || typeof filename !== "string") return - - // target only project files - if (!filename.includes(cwd)) return - - // inject projectPath variable - const body = path.node.body - const windowIdentifier = t.identifier("window") - const dataSourceLocIdentifier = t.identifier(WINDOW_PROJECTPATH_PROPERTY) - const dataSourceLocMemberExpression = t.memberExpression( - windowIdentifier, - dataSourceLocIdentifier, - ) - const cwdStringLiteral = t.stringLiteral(cwd) - const assignmentExpression = t.assignmentExpression( - "=", - dataSourceLocMemberExpression, - cwdStringLiteral, - ) - body.splice(0, 0, t.toStatement(assignmentExpression)) - }, - JSXOpeningElement(path, state) { - const container = path.container as t.JSXElement - if (container.openingElement.name.type !== "JSXIdentifier") return - const name = container.openingElement.name.name - - // Filter native elements - if (!isLowercase(name)) return - - const location = container.openingElement.loc - if (!location) return - - const { cwd, filename } = state as { cwd: unknown; filename: unknown } - if (typeof cwd !== "string" || typeof filename !== "string") return - - container.openingElement.attributes.push( - t.jsxAttribute( - t.jsxIdentifier(LOCATION_ATTRIBUTE_NAME), - t.stringLiteral( - getLocationAttribute( - relative(cwd, filename), - location.start.line, - // 2 is added to place the caret after the "<" character - location.start.column + 2, - ), - ), - ), - ) - }, - }, - } +export interface DevtoolsPluginOptions { + wrapStores?: boolean + jsxLocation?: boolean } // This export is used for configuration. -export const devtoolsPlugin = (): PluginOption => { +export const devtoolsPlugin = (options: DevtoolsPluginOptions = {}): PluginOption => { + const { wrapStores = false, jsxLocation = false } = options + let enablePlugin = false let projectRoot = process.cwd() @@ -81,9 +23,24 @@ export const devtoolsPlugin = (): PluginOption => { enablePlugin = config.command === "serve" && config.mode !== "production" }, async transform(source, id, transformOptions) { - // the plugin should only run on .tsx/.jsx files in development // production and server should be disabled - if (transformOptions?.ssr || !enablePlugin || !isFileJSX(id)) return + if (transformOptions?.ssr || !enablePlugin) return + + const extension = getFileExtension(id) + + if (!["js", "jsx", "ts", "tsx"].includes(extension)) return + + const isJSX = extension === "jsx" || extension === "tsx" + const plugins: PluginItem[] = [] + + // plugins that should only run on .tsx/.jsx files in development + if (jsxLocation && isJSX) plugins.push(jsxLocationPlugin) + if (wrapStores) plugins.push(wrapStoresPlugin) + + if (plugins.length === 0) return + + // babel doesn't work with typescript by default + plugins.splice(0, 0, ["@babel/plugin-syntax-typescript", { isTSX: isJSX }]) const result = await transformAsync(source, { babelrc: false, @@ -91,11 +48,9 @@ export const devtoolsPlugin = (): PluginOption => { root: projectRoot, filename: id, sourceFileName: id, - plugins: [ - ["@babel/plugin-syntax-typescript", { isTSX: true }], - "@solid-devtools/babel-plugin", - ], + plugins, }) + if (!result) return null const { code } = result if (!code) return null diff --git a/packages/babel-plugin/src/jsxLocation.ts b/packages/babel-plugin/src/jsxLocation.ts new file mode 100644 index 00000000..6f9811d5 --- /dev/null +++ b/packages/babel-plugin/src/jsxLocation.ts @@ -0,0 +1,61 @@ +import { relative } from "path" +import { PluginObj } from "@babel/core" +import * as t from "@babel/types" +import { LOCATION_ATTRIBUTE_NAME, WINDOW_PROJECTPATH_PROPERTY } from "@shared/variables" +import { getLocationAttribute, isLowercase, windowId } from "./utils" + +const jsxLocationPlugin: PluginObj = { + name: "@solid-devtools/jsx-location", + visitor: { + Program(path, state) { + const { cwd, filename } = state as { cwd: unknown; filename: unknown } + if (typeof cwd !== "string" || typeof filename !== "string") return + + // target only project files + if (!filename.includes(cwd)) return + + // inject projectPath variable + const body = path.node.body + + const dataSourceLocId = t.identifier(WINDOW_PROJECTPATH_PROPERTY) + const dataSourceLocMemberExpression = t.memberExpression(windowId, dataSourceLocId) + const cwdStringLiteral = t.stringLiteral(cwd) + const assignmentExpression = t.assignmentExpression( + "=", + dataSourceLocMemberExpression, + cwdStringLiteral, + ) + body.splice(0, 0, t.toStatement(assignmentExpression)) + }, + JSXOpeningElement(path, state) { + const container = path.container as t.JSXElement + if (container.openingElement.name.type !== "JSXIdentifier") return + const name = container.openingElement.name.name + + // Filter native elements + if (!isLowercase(name)) return + + const location = container.openingElement.loc + if (!location) return + + const { cwd, filename } = state as { cwd: unknown; filename: unknown } + if (typeof cwd !== "string" || typeof filename !== "string") return + + container.openingElement.attributes.push( + t.jsxAttribute( + t.jsxIdentifier(LOCATION_ATTRIBUTE_NAME), + t.stringLiteral( + getLocationAttribute( + relative(cwd, filename), + location.start.line, + // 2 is added to place the caret after the "<" character + location.start.column + 2, + ), + ), + ), + ) + }, + }, +} + +export default jsxLocationPlugin diff --git a/packages/babel-plugin/src/utils.ts b/packages/babel-plugin/src/utils.ts index df2cb7d6..acdcec56 100644 --- a/packages/babel-plugin/src/utils.ts +++ b/packages/babel-plugin/src/utils.ts @@ -1,3 +1,7 @@ +import * as t from "@babel/types" + +export const windowId = t.identifier("window") + const LOWERCASE_REGEX = /^[a-z0-9]+$/ export const isLowercase = (s: string) => LOWERCASE_REGEX.test(s) @@ -7,9 +11,5 @@ export const getLocationAttribute = (filePath: string, line: number, column: num export function getFileExtension(filename: string): string { const index = filename.lastIndexOf(".") - return index < 0 ? "" : filename.substring(index) -} -export function isFileJSX(filename: string): boolean { - const ext = getFileExtension(filename) - return ext === ".jsx" || ext === ".tsx" + return index < 0 ? "" : filename.substring(index + 1) } diff --git a/packages/babel-plugin/src/wrapStores.ts b/packages/babel-plugin/src/wrapStores.ts new file mode 100644 index 00000000..d779a5a6 --- /dev/null +++ b/packages/babel-plugin/src/wrapStores.ts @@ -0,0 +1,125 @@ +import { PluginObj } from "@babel/core" +import * as t from "@babel/types" +import { windowId } from "./utils" + +const createStoreId = t.identifier("createStore") +const overwriteName = "$sdt_createStore" +const overwriteNamespaceName = "$sdt_StoreNamespace" +const sdtCreateStoreId = t.identifier(overwriteName) +const overwriteNamespaceNameId = t.identifier(overwriteNamespaceName) + +/* +Store.createStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return $sdt_createStore(wrappedObj, options); +}; +*/ + +function getStoreWrapperArrowFn(): t.ArrowFunctionExpression { + const objId = t.identifier("obj") + const optionsId = t.identifier("options") + const wrappedObjId = t.identifier("wrappedObj") + const wrapStoreId = t.identifier("$sdt_wrapStore") + const wrapStoreMember = t.memberExpression(windowId, wrapStoreId) + + return t.arrowFunctionExpression( + [objId, optionsId], + t.blockStatement([ + t.variableDeclaration("let", [t.variableDeclarator(wrappedObjId, objId)]), + t.ifStatement( + t.binaryExpression( + "===", + t.unaryExpression("typeof", wrapStoreMember), + t.stringLiteral("function"), + ), + t.blockStatement([ + t.toStatement( + t.assignmentExpression("=", wrappedObjId, t.callExpression(wrapStoreMember, [objId])), + ), + ]), + ), + t.returnStatement(t.callExpression(sdtCreateStoreId, [wrappedObjId, optionsId])), + ]), + ) +} + +/** + * TODO: + * + * cover all import syntaxes + * - [x] import { createStore } from "solid-js/store" + * - [x] import { createStore as something } from "solid-js/store" + * - [x] import * as Store from "solid-js/store" + * + * cover all primitives + * - [x] createStore + * - [ ] createMutable + */ + +const wrapStoresPlugin: PluginObj = { + name: "@solid-devtools/wrap-stores", + visitor: { + Program(path) { + const body = path.node.body + + for (let importIndex = 0; importIndex < body.length; importIndex++) { + const node = body[importIndex] + if (node.type !== "ImportDeclaration" || node.source.value !== "solid-js/store") continue + + for (const s of node.specifiers) { + if (s.type === "ImportNamespaceSpecifier") { + const namespace = s.local.name + const namespaceId = t.identifier(namespace) + s.local.name = overwriteNamespaceName + + const original = t.variableDeclaration("const", [ + t.variableDeclarator( + sdtCreateStoreId, + t.memberExpression(overwriteNamespaceNameId, createStoreId), + ), + ]) + const overwriteNamespace = t.variableDeclaration("const", [ + t.variableDeclarator( + namespaceId, + t.objectExpression([t.spreadElement(overwriteNamespaceNameId)]), + ), + ]) + const overwrite = t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression(namespaceId, createStoreId), + getStoreWrapperArrowFn(), + ), + ) + + body.splice(importIndex + 1, 0, original, overwriteNamespace, overwrite) + return + } + if ( + s.type === "ImportSpecifier" && + (t.isIdentifier(s.imported) + ? s.imported.name === "createStore" + : s.imported.value === "createStore") + ) { + const userName = s.local.name + s.local.name = overwriteName + + const overwrite = t.variableDeclaration("const", [ + t.variableDeclarator(t.identifier(userName), getStoreWrapperArrowFn()), + ]) + + body.splice(importIndex + 1, 0, overwrite) + return + } + } + } + }, + }, +} + +export default wrapStoresPlugin diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 90d5601c..98968a67 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,4 +1,5 @@ import { Accessor, onCleanup, $PROXY, untrack, createEffect, on } from "solid-js" +import { $RAW } from "solid-js/store" import { arrayEquals, asArray, Many } from "@solid-primitives/utils" import { getOwnerType, @@ -527,3 +528,38 @@ export function debugProps(props: Record): void { ) } } + +export function debugStore(store: object): void { + if (!isSolidProxy(store)) { + console.warn("debugStore should be used with a proxy. Instead used on:", store) + return + } + const owner = getOwner() + if (!owner) { + console.warn("debugStore should be used synchronously inside an owner") + return + } + const argTarget = (store as any)[$RAW] + // console.log(argTarget) + + if (owner.sourceMap) { + // Object.defineProperty(argTarget, $RAW, { + // get() { + // console.log("ASDFGSDGDRHDZ") + // return argTarget + // }, + // }) + // const ownerTarget = Object.values(owner.sourceMap).find( + // s => !("name" in s) && s.value === argTarget, + // ) + // if (ownerTarget) { + // ownerTarget.value = { + // 0: { + // title: "Ahahaha", + // done: true, + // }, + // } + // console.log(ownerTarget) + // } + } +} diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index a2b1caab..688ddefe 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -19,6 +19,7 @@ "dependencies": { "@solid-devtools/logger": "^0.2.0", "@solid-primitives/timer": "^1.3.1", + "object-observer": "^5.0.4", "solid-devtools": "^0.8.1", "solid-js": "^1.4.8" } diff --git a/playgrounds/sandbox/src/App.tsx b/playgrounds/sandbox/src/App.tsx index 33cd8e36..af63c3a5 100644 --- a/playgrounds/sandbox/src/App.tsx +++ b/playgrounds/sandbox/src/App.tsx @@ -1,4 +1,3 @@ -/* @refresh reload */ import { makeTimer } from "@solid-primitives/timer" import { debugOwnerComputations, debugOwnerSignals, debugProps } from "@solid-devtools/logger" import { diff --git a/playgrounds/sandbox/src/Todos.tsx b/playgrounds/sandbox/src/Todos.tsx index e0ee9dbb..1e60ef2c 100644 --- a/playgrounds/sandbox/src/Todos.tsx +++ b/playgrounds/sandbox/src/Todos.tsx @@ -1,14 +1,14 @@ -import { debugProps } from "@solid-devtools/logger" +import { debugProps, debugStore } from "@solid-devtools/logger" import { createEffect, createSignal, batch, For, Component } from "solid-js" import { reconcile } from "solid-js/store" -import { createStore, SetStoreFunction, Store } from "solid-js/store" +import * as Store from "solid-js/store" export function createLocalStore( name: string, init: T, -): [Store, SetStoreFunction] { +): [Store.Store, Store.SetStoreFunction] { const localState = localStorage.getItem(name) - const [state, setState] = createStore(localState ? JSON.parse(localState) : init) + const [state, setState] = Store.createStore(localState ? JSON.parse(localState) : init) createEffect(() => localStorage.setItem(name, JSON.stringify(state))) return [state, setState] } @@ -26,7 +26,7 @@ const Todo: Component<{ onUpdate: (value: string) => void onRemove: VoidFunction }> = props => { - debugProps(props) + // debugProps(props) return (
@@ -49,6 +49,8 @@ const Todos: Component = () => { const [newTitle, setTitle] = createSignal("") const [todos, setTodos] = createLocalStore("todos", []) + // debugStore(todos) + const addTodo = (e: SubmitEvent) => { e.preventDefault() batch(() => { @@ -60,16 +62,16 @@ const Todos: Component = () => { }) } - setTimeout(() => { - setTodos( - 0, - reconcile({ - title: "Learn Solid-JS", - done: false, - [Math.random() + ""]: "hello", - }), - ) - }, 1000) + // setTimeout(() => { + // setTodos( + // 0, + // reconcile({ + // title: "Learn Solid-JS", + // done: false, + // [Math.random() + ""]: "hello", + // }), + // ) + // }, 1000) return ( <> diff --git a/playgrounds/sandbox/src/index.tsx b/playgrounds/sandbox/src/index.tsx index c6561c1a..3a9cc5ba 100644 --- a/playgrounds/sandbox/src/index.tsx +++ b/playgrounds/sandbox/src/index.tsx @@ -1,6 +1,18 @@ /* @refresh reload */ import { render } from "solid-js/web" import { useLocatorPlugin } from "solid-devtools" +import { Observable } from "object-observer" + +window["$sdt_wrapStore"] = obj => { + console.log("storeCreated") + const observed = Observable.from(obj) + Observable.observe(observed, changes => { + changes.forEach(change => { + console.log(change) + }) + }) + return observed +} import App from "./App" diff --git a/playgrounds/sandbox/vite.config.ts b/playgrounds/sandbox/vite.config.ts index 07204bee..9210abc1 100644 --- a/playgrounds/sandbox/vite.config.ts +++ b/playgrounds/sandbox/vite.config.ts @@ -22,7 +22,6 @@ export default defineConfig({ }, build: { target: "esnext", - polyfillDynamicImport: false, }, optimizeDeps: { exclude: ["solid-devtools/vite"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 763229b3..dd5f7c3a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -316,6 +316,7 @@ importers: '@solid-devtools/babel-plugin': ^0.3.1 '@solid-devtools/logger': ^0.2.0 '@solid-primitives/timer': ^1.3.1 + object-observer: ^5.0.4 solid-devtools: ^0.8.1 solid-js: ^1.4.8 typescript: ^4.7.4 @@ -324,6 +325,7 @@ importers: dependencies: '@solid-devtools/logger': link:../../packages/logger '@solid-primitives/timer': 1.3.1_solid-js@1.4.8 + object-observer: 5.0.4 solid-devtools: link:../../packages/main solid-js: 1.4.8 devDependencies: @@ -8397,6 +8399,10 @@ packages: engines: {node: '>= 0.4'} dev: true + /object-observer/5.0.4: + resolution: {integrity: sha512-8frW1FqyYcefFBsivw6VQ/TrxmsqGHIDzzaRhov0kBxd7OvmDXNtf7pUH45/+uif24VLEwIGwqJ3IPITWojaKg==} + dev: false + /object.assign/4.1.2: resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} engines: {node: '>= 0.4'} From 50dce82a38dcc2d814997325a3d4f5e4f34f19f3 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:23:51 +0200 Subject: [PATCH 02/13] improve wrapStores transform and test it --- packages/babel-plugin/jest.config.ts | 2 + packages/babel-plugin/package.json | 6 +- packages/babel-plugin/src/utils.ts | 2 + packages/babel-plugin/src/wrapStores.ts | 209 ++++++++++-------- packages/babel-plugin/test/wrapStores.test.ts | 153 +++++++++++++ pnpm-lock.yaml | 13 +- 6 files changed, 285 insertions(+), 100 deletions(-) create mode 100644 packages/babel-plugin/jest.config.ts create mode 100644 packages/babel-plugin/test/wrapStores.test.ts diff --git a/packages/babel-plugin/jest.config.ts b/packages/babel-plugin/jest.config.ts new file mode 100644 index 00000000..7f8a8463 --- /dev/null +++ b/packages/babel-plugin/jest.config.ts @@ -0,0 +1,2 @@ +import config from "../../configs/jest.config" +export default config diff --git a/packages/babel-plugin/package.json b/packages/babel-plugin/package.json index d01f46e8..db00cba4 100644 --- a/packages/babel-plugin/package.json +++ b/packages/babel-plugin/package.json @@ -29,14 +29,18 @@ ], "scripts": { "dev": "tsup --watch", - "build": "tsup" + "build": "tsup", + "test": "jest --config jest.config.ts", + "typecheck": "tsc --noEmit" }, "devDependencies": { "@babel/core": "^7.18.10", + "@babel/generator": "^7.18.10", "@babel/parser": "^7.18.10", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/traverse": "^7.18.10", "@types/babel__core": "^7.1.19", + "@types/babel__generator": "^7.6.4", "@types/babel__traverse": "^7.17.1", "tsup": "^6.2.1", "typescript": "^4.7.4", diff --git a/packages/babel-plugin/src/utils.ts b/packages/babel-plugin/src/utils.ts index acdcec56..37663fbb 100644 --- a/packages/babel-plugin/src/utils.ts +++ b/packages/babel-plugin/src/utils.ts @@ -1,6 +1,8 @@ import * as t from "@babel/types" export const windowId = t.identifier("window") +export const storeOverwriteName = "$sdt_createStore" +export const storeOverwriteNamespace = "$sdt_StoreNamespace" const LOWERCASE_REGEX = /^[a-z0-9]+$/ diff --git a/packages/babel-plugin/src/wrapStores.ts b/packages/babel-plugin/src/wrapStores.ts index d779a5a6..7a7b6788 100644 --- a/packages/babel-plugin/src/wrapStores.ts +++ b/packages/babel-plugin/src/wrapStores.ts @@ -1,12 +1,9 @@ import { PluginObj } from "@babel/core" import * as t from "@babel/types" -import { windowId } from "./utils" +import { storeOverwriteName, storeOverwriteNamespace, windowId } from "./utils" const createStoreId = t.identifier("createStore") -const overwriteName = "$sdt_createStore" -const overwriteNamespaceName = "$sdt_StoreNamespace" -const sdtCreateStoreId = t.identifier(overwriteName) -const overwriteNamespaceNameId = t.identifier(overwriteNamespaceName) +const createMutableId = t.identifier("createMutable") /* Store.createStore = (obj, options) => { @@ -20,106 +17,128 @@ Store.createStore = (obj, options) => { }; */ -function getStoreWrapperArrowFn(): t.ArrowFunctionExpression { - const objId = t.identifier("obj") - const optionsId = t.identifier("options") - const wrappedObjId = t.identifier("wrappedObj") - const wrapStoreId = t.identifier("$sdt_wrapStore") - const wrapStoreMember = t.memberExpression(windowId, wrapStoreId) +function getStoreWrapperArrowFn(overwrite: t.Identifier): t.ArrowFunctionExpression { + const objId = t.identifier("obj") + const optionsId = t.identifier("options") + const wrappedObjId = t.identifier("wrappedObj") + const wrapStoreId = t.identifier("$sdt_wrapStore") + const wrapStoreMember = t.memberExpression(windowId, wrapStoreId) - return t.arrowFunctionExpression( - [objId, optionsId], - t.blockStatement([ - t.variableDeclaration("let", [t.variableDeclarator(wrappedObjId, objId)]), - t.ifStatement( - t.binaryExpression( - "===", - t.unaryExpression("typeof", wrapStoreMember), - t.stringLiteral("function"), - ), - t.blockStatement([ - t.toStatement( - t.assignmentExpression("=", wrappedObjId, t.callExpression(wrapStoreMember, [objId])), - ), - ]), - ), - t.returnStatement(t.callExpression(sdtCreateStoreId, [wrappedObjId, optionsId])), - ]), - ) + return t.arrowFunctionExpression( + [objId, optionsId], + t.blockStatement([ + t.variableDeclaration("let", [t.variableDeclarator(wrappedObjId, objId)]), + t.ifStatement( + t.binaryExpression( + "===", + t.unaryExpression("typeof", wrapStoreMember), + t.stringLiteral("function"), + ), + t.blockStatement([ + t.toStatement( + t.assignmentExpression("=", wrappedObjId, t.callExpression(wrapStoreMember, [objId])), + ), + ]), + ), + t.returnStatement(t.callExpression(overwrite, [wrappedObjId, optionsId])), + ]), + ) } -/** - * TODO: - * - * cover all import syntaxes - * - [x] import { createStore } from "solid-js/store" - * - [x] import { createStore as something } from "solid-js/store" - * - [x] import * as Store from "solid-js/store" - * - * cover all primitives - * - [x] createStore - * - [ ] createMutable - */ - const wrapStoresPlugin: PluginObj = { - name: "@solid-devtools/wrap-stores", - visitor: { - Program(path) { - const body = path.node.body + name: "@solid-devtools/wrap-stores", + visitor: { + Program(path) { + const body = path.node.body + let IdSuffix = 0 - for (let importIndex = 0; importIndex < body.length; importIndex++) { - const node = body[importIndex] - if (node.type !== "ImportDeclaration" || node.source.value !== "solid-js/store") continue + for (let line = 0; line < body.length; line++) { + const node = body[line] + if (node.type !== "ImportDeclaration" || node.source.value !== "solid-js/store") continue - for (const s of node.specifiers) { - if (s.type === "ImportNamespaceSpecifier") { - const namespace = s.local.name - const namespaceId = t.identifier(namespace) - s.local.name = overwriteNamespaceName + for (const s of node.specifiers) { + // import * as Store from "solid-js/store" + if (s.type === "ImportNamespaceSpecifier") { + const namespaceId = t.identifier(s.local.name) + s.local.name = storeOverwriteNamespace - const original = t.variableDeclaration("const", [ - t.variableDeclarator( - sdtCreateStoreId, - t.memberExpression(overwriteNamespaceNameId, createStoreId), - ), - ]) - const overwriteNamespace = t.variableDeclaration("const", [ - t.variableDeclarator( - namespaceId, - t.objectExpression([t.spreadElement(overwriteNamespaceNameId)]), - ), - ]) - const overwrite = t.expressionStatement( - t.assignmentExpression( - "=", - t.memberExpression(namespaceId, createStoreId), - getStoreWrapperArrowFn(), - ), - ) + const overwriteStoreId = t.identifier(`${storeOverwriteName}${IdSuffix++}`) + const overwriteMutableId = t.identifier(`${storeOverwriteName}${IdSuffix++}`) + const originalStore = t.variableDeclaration("const", [ + t.variableDeclarator( + overwriteStoreId, + t.memberExpression(t.identifier(storeOverwriteNamespace), createStoreId), + ), + ]) + const originalMutable = t.variableDeclaration("const", [ + t.variableDeclarator( + overwriteMutableId, + t.memberExpression(t.identifier(storeOverwriteNamespace), createMutableId), + ), + ]) + const overwriteNamespace = t.variableDeclaration("const", [ + t.variableDeclarator( + namespaceId, + t.objectExpression([t.spreadElement(t.identifier(storeOverwriteNamespace))]), + ), + ]) + const overwriteStore = t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression(namespaceId, createStoreId), + getStoreWrapperArrowFn(overwriteStoreId), + ), + ) + const overwriteMutable = t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression(namespaceId, createMutableId), + getStoreWrapperArrowFn(overwriteMutableId), + ), + ) - body.splice(importIndex + 1, 0, original, overwriteNamespace, overwrite) - return - } - if ( - s.type === "ImportSpecifier" && - (t.isIdentifier(s.imported) - ? s.imported.name === "createStore" - : s.imported.value === "createStore") - ) { - const userName = s.local.name - s.local.name = overwriteName + // insert after import statement + body.splice( + line + 1, + 0, + originalStore, + originalMutable, + overwriteNamespace, + overwriteStore, + overwriteMutable, + ) + line += 5 + } + // import { createStore } from "solid-js/store" + // import { createStore as userName } from "solid-js/store" + let primitive: "createStore" | "createMutable" + if ( + s.type === "ImportSpecifier" && + ([(s.imported as t.Identifier).name, (s.imported as t.StringLiteral).value].includes( + (primitive = "createStore"), + ) || + [(s.imported as t.Identifier).name, (s.imported as t.StringLiteral).value].includes( + (primitive = "createMutable"), + )) + ) { + const userName = s.local.name + const overwriteName = `${storeOverwriteName}${IdSuffix++}` + s.local.name = overwriteName - const overwrite = t.variableDeclaration("const", [ - t.variableDeclarator(t.identifier(userName), getStoreWrapperArrowFn()), - ]) + const overwrite = t.variableDeclaration("const", [ + t.variableDeclarator( + t.identifier(userName), + getStoreWrapperArrowFn(t.identifier(overwriteName)), + ), + ]) - body.splice(importIndex + 1, 0, overwrite) - return - } - } - } - }, - }, + // insert after import statement + body.splice(++line, 0, overwrite) + } + } + } + }, + }, } export default wrapStoresPlugin diff --git a/packages/babel-plugin/test/wrapStores.test.ts b/packages/babel-plugin/test/wrapStores.test.ts new file mode 100644 index 00000000..7ea54bb4 --- /dev/null +++ b/packages/babel-plugin/test/wrapStores.test.ts @@ -0,0 +1,153 @@ +import { traverse } from "@babel/core" +import generate from "@babel/generator" +import { parse } from "@babel/parser" +import { storeOverwriteName, storeOverwriteNamespace } from "../src/utils" +import plugin from "../src/wrapStores" + +function assertTransform(src: string, expectedOutput: string) { + const ast = parse(src, { + sourceType: "module", + plugins: ["jsx"], + }) + + traverse(ast, plugin.visitor) + const res = generate(ast) + + expect(res.code).toBe(expectedOutput) +} + +describe("createStore", () => { + test("named import", () => { + const src = /*javascript*/ `import { createStore } from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; + +const createStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) + }) + + test("renamed import", () => { + const src = /*javascript*/ `import { createStore as createSolidStore } from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; + +const createSolidStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) + }) +}) + +describe("createMutable", () => { + test("named import", () => { + const src = /*javascript*/ `import { createMutable } from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; + +const createMutable = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) + }) + + test("renamed import", () => { + const src = /*javascript*/ `import { createMutable as createSolidStore } from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; + +const createSolidStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) + }) +}) + +test("namespace import", () => { + const src = /*javascript*/ `import * as Store from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import * as ${storeOverwriteNamespace} from "solid-js/store"; +const ${storeOverwriteName}0 = ${storeOverwriteNamespace}.createStore; +const ${storeOverwriteName}1 = ${storeOverwriteNamespace}.createMutable; +const Store = { ...${storeOverwriteNamespace} +}; + +Store.createStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +}; + +Store.createMutable = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}1(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) +}) + +test("both", () => { + const src = /*javascript*/ `import { createMutable, createStore } from "solid-js/store";` + + const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0, createStore as ${storeOverwriteName}1 } from "solid-js/store"; + +const createMutable = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}0(wrappedObj, options); +}; + +const createStore = (obj, options) => { + let wrappedObj = obj; + + if (typeof window.$sdt_wrapStore === "function") { + wrappedObj = window.$sdt_wrapStore(obj); + } + + return ${storeOverwriteName}1(wrappedObj, options); +};` + + assertTransform(src, expectedOutput) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd5f7c3a..71e7c9cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,12 +41,14 @@ importers: packages/babel-plugin: specifiers: '@babel/core': ^7.18.10 + '@babel/generator': ^7.18.10 '@babel/parser': ^7.18.10 '@babel/plugin-syntax-jsx': ^7.18.6 '@babel/plugin-syntax-typescript': ^7.18.6 '@babel/traverse': ^7.18.10 '@babel/types': ^7.18.10 '@types/babel__core': ^7.1.19 + '@types/babel__generator': ^7.6.4 '@types/babel__traverse': ^7.17.1 solid-js: ^1.4.4 tsup: ^6.2.1 @@ -58,10 +60,12 @@ importers: '@babel/types': 7.18.10 solid-js: 1.4.5 devDependencies: + '@babel/generator': 7.18.10 '@babel/parser': 7.18.10 '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.10 '@babel/traverse': 7.18.10 '@types/babel__core': 7.1.19 + '@types/babel__generator': 7.6.4 '@types/babel__traverse': 7.17.1 tsup: 6.2.1_typescript@4.7.4 typescript: 4.7.4 @@ -835,7 +839,7 @@ packages: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.7 + '@babel/types': 7.18.10 /@babel/helper-member-expression-to-functions/7.17.7: resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} @@ -876,7 +880,7 @@ packages: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.7 + '@babel/types': 7.18.10 /@babel/helper-module-transforms/7.18.0: resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==} @@ -1044,7 +1048,7 @@ packages: resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.7 + '@babel/types': 7.18.10 /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} @@ -1071,7 +1075,7 @@ packages: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.7 + '@babel/types': 7.18.10 /@babel/helper-string-parser/7.18.10: resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} @@ -3227,6 +3231,7 @@ packages: dependencies: '@babel/helper-validator-identifier': 7.18.6 to-fast-properties: 2.0.0 + dev: true /@bcoe/v8-coverage/0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} From 8112efc0409a10565e979bd31040fe22cf4ba85f Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:24:14 +0200 Subject: [PATCH 03/13] switch to space indent --- .prettierrc | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.prettierrc b/.prettierrc index d1c20d7c..0f762609 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,15 +4,7 @@ "printWidth": 100, "semi": false, "singleQuote": false, - "useTabs": true, + "useTabs": false, "arrowParens": "avoid", - "bracketSpacing": true, - "overrides": [ - { - "files": "*.md", - "options": { - "useTabs": false - } - } - ] + "bracketSpacing": true } From b285f3f03a73d271f0544bdf3fa6a94a94bbfde7 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:24:33 +0200 Subject: [PATCH 04/13] tab -> space --- .editorconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index ef8b2d93..0f099897 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,9 +2,9 @@ root = true [*] -indent_style = tab +indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true \ No newline at end of file +insert_final_newline = true From 1aab4dae98bb3719279d7f5a56f13494255cd2f5 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:27:30 +0200 Subject: [PATCH 05/13] add jest depdendency to babel-plugin pkg --- packages/babel-plugin/package.json | 113 ++++++++++++------------- pnpm-lock.yaml | 127 ++++++++++++++++++++++++++--- 2 files changed, 171 insertions(+), 69 deletions(-) diff --git a/packages/babel-plugin/package.json b/packages/babel-plugin/package.json index db00cba4..b43d4dc0 100644 --- a/packages/babel-plugin/package.json +++ b/packages/babel-plugin/package.json @@ -1,58 +1,59 @@ { - "name": "@solid-devtools/babel-plugin", - "version": "0.3.1", - "description": "Babel plugin for transforming SolidJS code in development to enchance solid-devtools usage.", - "license": "MIT", - "author": "Damian Tarnawski ", - "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/babel-plugin#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/thetarnav/solid-devtools.git" - }, - "bugs": { - "url": "https://github.com/thetarnav/solid-devtools/issues" - }, - "keywords": [ - "solid", - "devtools", - "components", - "babel", - "plugin", - "jsx" - ], - "private": false, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "dev": "tsup --watch", - "build": "tsup", - "test": "jest --config jest.config.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/generator": "^7.18.10", - "@babel/parser": "^7.18.10", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/traverse": "^7.18.10", - "@types/babel__core": "^7.1.19", - "@types/babel__generator": "^7.6.4", - "@types/babel__traverse": "^7.17.1", - "tsup": "^6.2.1", - "typescript": "^4.7.4", - "vite": "^3.0.4" - }, - "dependencies": { - "@babel/core": "^7.18.10", - "@babel/plugin-syntax-typescript": "^7.18.6", - "@babel/types": "^7.18.10" - }, - "peerDependencies": { - "solid-js": "^1.4.4", - "vite": "^3.0.0" - } + "name": "@solid-devtools/babel-plugin", + "version": "0.3.1", + "description": "Babel plugin for transforming SolidJS code in development to enchance solid-devtools usage.", + "license": "MIT", + "author": "Damian Tarnawski ", + "contributors": [], + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/babel-plugin#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thetarnav/solid-devtools.git" + }, + "bugs": { + "url": "https://github.com/thetarnav/solid-devtools/issues" + }, + "keywords": [ + "solid", + "devtools", + "components", + "babel", + "plugin", + "jsx" + ], + "private": false, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "test": "jest --config jest.config.ts", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@babel/core": "^7.18.10", + "@babel/generator": "^7.18.10", + "@babel/parser": "^7.18.10", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/traverse": "^7.18.10", + "@types/babel__core": "^7.1.19", + "@types/babel__generator": "^7.6.4", + "@types/babel__traverse": "^7.17.1", + "tsup": "^6.2.1", + "typescript": "^4.7.4", + "vite": "^3.0.4", + "jest": "^28.1.3" + }, + "dependencies": { + "@babel/core": "^7.18.10", + "@babel/plugin-syntax-typescript": "^7.18.6", + "@babel/types": "^7.18.10" + }, + "peerDependencies": { + "solid-js": "^1.4.4", + "vite": "^3.0.0" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71e7c9cc..ac375ecc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,7 @@ importers: '@types/babel__core': ^7.1.19 '@types/babel__generator': ^7.6.4 '@types/babel__traverse': ^7.17.1 + jest: ^28.1.3 solid-js: ^1.4.4 tsup: ^6.2.1 typescript: ^4.7.4 @@ -67,6 +68,7 @@ importers: '@types/babel__core': 7.1.19 '@types/babel__generator': 7.6.4 '@types/babel__traverse': 7.17.1 + jest: 28.1.3 tsup: 6.2.1_typescript@4.7.4 typescript: 4.7.4 vite: 3.0.4 @@ -327,7 +329,7 @@ importers: vite: ^3.0.4 vite-plugin-solid: ^2.3.0 dependencies: - '@solid-devtools/logger': link:../../packages/logger + '@solid-devtools/logger': 0.2.1_solid-js@1.4.8 '@solid-primitives/timer': 1.3.1_solid-js@1.4.8 object-observer: 5.0.4 solid-devtools: link:../../packages/main @@ -353,7 +355,7 @@ importers: vite: 2.9.12 devDependencies: '@solid-devtools/babel-plugin': link:../../packages/babel-plugin - '@solid-devtools/logger': link:../../packages/logger + '@solid-devtools/logger': 0.2.1_solid-js@1.4.8 solid-app-router: 0.4.2_solid-js@1.4.8 solid-devtools: link:../../packages/main solid-js: 1.4.8 @@ -4107,6 +4109,28 @@ packages: solid-js: 1.4.8 dev: false + /@solid-devtools/debugger/0.2.3_solid-js@1.4.8: + resolution: {integrity: sha512-zFWf8iT+26gKT7l1tA7prTLtPUlANUFFpwSTCKxEk5nE4ZmygnLEy7OJdGZGL29c8/pJNjrxmDmfpWWaKDfjAA==} + peerDependencies: + solid-js: ^1.4.5 + dependencies: + '@solid-primitives/event-bus': 0.1.0_solid-js@1.4.8 + '@solid-primitives/immutable': 0.1.0_solid-js@1.4.8 + '@solid-primitives/memo': 0.3.0_solid-js@1.4.8 + '@solid-primitives/refs': 0.3.0_solid-js@1.4.8 + '@solid-primitives/scheduled': 1.0.0_solid-js@1.4.8 + '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 + solid-js: 1.4.8 + + /@solid-devtools/logger/0.2.1_solid-js@1.4.8: + resolution: {integrity: sha512-H30lRefVz6xFBAiepuKxBiu36XUY32FD5Sdekc47rKTwh/LjCZvmrJid/rr3dP6mOQAHiTSRdklztyDoK4D3ng==} + peerDependencies: + solid-js: ^1.4.4 + dependencies: + '@solid-devtools/debugger': 0.2.3_solid-js@1.4.8 + '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 + solid-js: 1.4.8 + /@solid-primitives/bounds/0.0.100_solid-js@1.4.8: resolution: {integrity: sha512-UkIzY2FHow7zZzacrqByOMYxFTpg3mLnyZ0rVOxLlJP4G0/E+ig+ulh5tlTssRuxvs54m9dEOHkG7Is6DUNPEQ==} peerDependencies: @@ -4134,7 +4158,6 @@ packages: '@solid-primitives/immutable': 0.1.0_solid-js@1.4.8 '@solid-primitives/utils': 1.5.2_solid-js@1.4.8 solid-js: 1.4.8 - dev: false /@solid-primitives/event-listener/2.2.1_solid-js@1.4.8: resolution: {integrity: sha512-KPHi/kkMtWGbM7fVNez9ut5MXTT2lnvsGxnVFJXaqLpFiDliZzk3Z2rpi78B7BqEaqn7msAV/UdJ4omJPKSDJA==} @@ -4151,7 +4174,6 @@ packages: '@solid-primitives/utils': 1.5.2_solid-js@1.4.8 transitivePeerDependencies: - solid-js - dev: false /@solid-primitives/keyboard/0.0.100_solid-js@1.4.8: resolution: {integrity: sha512-rdQdpOvHm6zde3lapYYT0xwiTvnQtMszHjia7u6qrb5VJLnw77wa9YigJNjdL8YYRcl6NLKZg/wLr5yfjqKVJA==} @@ -4170,7 +4192,6 @@ packages: '@solid-primitives/scheduled': 1.0.0_solid-js@1.4.8 '@solid-primitives/utils': 2.1.1_solid-js@1.4.8 solid-js: 1.4.8 - dev: false /@solid-primitives/platform/0.0.100_solid-js@1.4.8: resolution: {integrity: sha512-TMGeDtjA8b7xlUQGq3QhdR8SSa49bvqhFJ0iD+iy4fsnEHr9xA5hIDyBX/ntmz70SbOVyz+z9IdXwBnqurr4bQ==} @@ -4198,7 +4219,6 @@ packages: '@solid-primitives/rootless': 1.1.0_solid-js@1.4.8 '@solid-primitives/utils': 1.5.2_solid-js@1.4.8 solid-js: 1.4.8 - dev: false /@solid-primitives/resize-observer/2.0.2_solid-js@1.4.8: resolution: {integrity: sha512-TAOKoRwCp2ovN9jFgca/BkPA4IRYO1/SNJ9zZr95cuOTI9j8BR2N1h4+Py5gehq1ZCFryatIB5/7qrqXpZus/w==} @@ -4218,7 +4238,6 @@ packages: dependencies: '@solid-primitives/utils': 1.5.2_solid-js@1.4.8 solid-js: 1.4.8 - dev: false /@solid-primitives/rootless/1.1.2_solid-js@1.4.8: resolution: {integrity: sha512-CSOM3+KUXiim/EqXTnfgQSZ0sAev9bd+6U8eME3amrYwZIxVF46tmYTAig/GZa3nLylmyokwqYStlcwCgSMQpQ==} @@ -4235,7 +4254,6 @@ packages: solid-js: ^1.4.0 dependencies: solid-js: 1.4.8 - dev: false /@solid-primitives/timer/1.3.1_solid-js@1.4.8: resolution: {integrity: sha512-OUxnq96WUCydpYHg8LYv/93Tjoz7jUgaXyNLZWfN9tZ7+QrOhHg4lntzQRBWK99mYCPqZF5IX0SAcY6IO9pRjw==} @@ -4251,7 +4269,6 @@ packages: solid-js: ^1.3.1 dependencies: solid-js: 1.4.8 - dev: false /@solid-primitives/utils/2.1.1_solid-js@1.4.8: resolution: {integrity: sha512-im/WJDgLnJgjuEhB2a46RgDU/ODoc4g+FMySjJ8erPTFrhyTwIe5wv9MSUf7SYsguLe4rfQirNEKLSSyLPETkw==} @@ -4259,7 +4276,6 @@ packages: solid-js: ^1.4.1 dependencies: solid-js: 1.4.8 - dev: false /@solid-primitives/utils/3.0.1_solid-js@1.4.8: resolution: {integrity: sha512-tQl0z8HG+iJ7eryq5KmRkr9xNk9oU06gOUQ37YKod0N5oPaWEvP+ATzi7Uo7w5L67wjaZuQCt80p9vlm1/J/Mg==} @@ -4267,7 +4283,6 @@ packages: solid-js: ^1.4.1 dependencies: solid-js: 1.4.8 - dev: false /@testing-library/jest-dom/5.16.4: resolution: {integrity: sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==} @@ -5599,7 +5614,7 @@ packages: dev: true /ee-first/1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} dev: true /electron-to-chromium/1.4.139: @@ -7192,6 +7207,34 @@ packages: - supports-color dev: true + /jest-cli/28.1.3: + resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + import-local: 3.1.0 + jest-config: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + prompts: 2.4.2 + yargs: 17.5.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jest-cli/28.1.3_@types+node@17.0.45: resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -7248,6 +7291,44 @@ packages: - ts-node dev: true + /jest-config/28.1.3: + resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.18.10 + '@jest/test-sequencer': 28.1.3 + '@jest/types': 28.1.3 + babel-jest: 28.1.3_@babel+core@7.18.10 + chalk: 4.1.2 + ci-info: 3.3.2 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-circus: 28.1.3 + jest-environment-node: 28.1.3 + jest-get-type: 28.0.2 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-runner: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 28.1.3 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /jest-config/28.1.3_@types+node@17.0.45: resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -7698,6 +7779,26 @@ packages: supports-color: 8.1.1 dev: true + /jest/28.1.3: + resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 28.1.3 + '@jest/types': 28.1.3 + import-local: 3.1.0 + jest-cli: 28.1.3 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jest/28.1.3_@types+node@17.0.45: resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10149,7 +10250,7 @@ packages: dev: true /utils-merge/1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} engines: {node: '>= 0.4.0'} dev: true From b12f88960831e28c9f9765aeea9076bce1b8a011 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:31:25 +0200 Subject: [PATCH 06/13] logger: remove debugStore hook --- packages/logger/src/index.ts | 811 +++++++++++++++++------------------ 1 file changed, 405 insertions(+), 406 deletions(-) diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 98968a67..4e433cdb 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,50 +1,49 @@ import { Accessor, onCleanup, $PROXY, untrack, createEffect, on } from "solid-js" -import { $RAW } from "solid-js/store" import { arrayEquals, asArray, Many } from "@solid-primitives/utils" import { - getOwnerType, - isSolidComputation, - observeValueUpdate, - onParentCleanup, - getFunctionSources, - makeSolidUpdateListener, - isSolidMemo, - interceptComputationRerun, - lookupOwner, + getOwnerType, + isSolidComputation, + observeValueUpdate, + onParentCleanup, + getFunctionSources, + makeSolidUpdateListener, + isSolidMemo, + interceptComputationRerun, + lookupOwner, } from "@solid-devtools/debugger" import { SignalState, Owner } from "@shared/solid" import { getOwner, NodeType, SolidComputation, SolidOwner, SolidSignal } from "@shared/graph" import { dedupeArray, arrayRefEquals } from "@shared/utils" import { - getComputationCreatedLabel, - getComputationRerunLabel, - getOwnerDisposedLabel, - getNodeState, - logComputation, - logInitialValue, - logObservers, - logOwned, - logSignalsInitialValues, - logSignalValueUpdate, - UNUSED, - NodeStateWithValue, - paddedForEach, - getPropsInitLabel, - logSignalValue, - getPropsKeyUpdateLabel, - getPropLabel, + getComputationCreatedLabel, + getComputationRerunLabel, + getOwnerDisposedLabel, + getNodeState, + logComputation, + logInitialValue, + logObservers, + logOwned, + logSignalsInitialValues, + logSignalValueUpdate, + UNUSED, + NodeStateWithValue, + paddedForEach, + getPropsInitLabel, + logSignalValue, + getPropsKeyUpdateLabel, + getPropLabel, } from "./log" import { getDiffMap, makeTimeMeter } from "./utils" declare module "solid-js/types/reactive/signal" { - interface Owner { - $debug?: boolean - $debugSignals?: boolean - $debugOwned?: boolean - } - interface SignalState { - $debugSignal?: boolean - } + interface Owner { + $debug?: boolean + $debugSignals?: boolean + $debugOwned?: boolean + } + interface SignalState { + $debugSignal?: boolean + } } const isSolidProxy = (o: any): boolean => !!o[$PROXY] @@ -55,23 +54,23 @@ const isSolidProxy = (o: any): boolean => !!o[$PROXY] function markDebugNode(o: Owner, type: "computation" | "signals" | "owned"): true | VoidFunction function markDebugNode(o: SignalState): true | VoidFunction function markDebugNode( - o: Owner | SignalState, - type?: "computation" | "signals" | "owned", + o: Owner | SignalState, + type?: "computation" | "signals" | "owned", ): true | VoidFunction { - let property: "$debug" | "$debugSignals" | "$debugOwned" | "$debugSignal" - if (type === "computation") property = "$debug" - else if (type === "signals") property = "$debugSignals" - else if (type === "owned") property = "$debugOwned" - else property = "$debugSignal" - - if ((o as any)[property]) return true - ;(o as any)[property] = true - return () => ((o as any)[property] = false) + let property: "$debug" | "$debugSignals" | "$debugOwned" | "$debugSignal" + if (type === "computation") property = "$debug" + else if (type === "signals") property = "$debugSignals" + else if (type === "owned") property = "$debugOwned" + else property = "$debugSignal" + + if ((o as any)[property]) return true + ;(o as any)[property] = true + return () => ((o as any)[property] = false) } interface DebugComputationOptions { - /** hook called during initial computation run? *(Defaults to `true`)* */ - initialRun?: boolean + /** hook called during initial computation run? *(Defaults to `true`)* */ + initialRun?: boolean } /** @@ -94,101 +93,101 @@ interface DebugComputationOptions { */ export function debugComputation(owner?: Owner, options?: DebugComputationOptions): void export function debugComputation( - _owner?: Owner, - { initialRun = true }: DebugComputationOptions = {}, + _owner?: Owner, + { initialRun = true }: DebugComputationOptions = {}, ): void { - const owner = _owner === undefined ? getOwner() : (_owner as SolidOwner) - if (!owner || !isSolidComputation(owner)) return console.warn("owner is not a computation") - - if (markDebugNode(owner, "computation") === true) return - - const { type, typeName, name } = getNodeState(owner) - const SYMBOL = Symbol(name) - // log prev value only of the computation callback uses it - const usesPrev = !!owner.fn.length - const usesValue = usesPrev || type === NodeType.Memo - - let updateListeners: VoidFunction[] = [] - let signalUpdates: NodeStateWithValue[] = [] - - // patches source objects to track their value updates - // will unsubscribe from previous sources on each call - const observeSources = (sources: (SolidComputation | SolidSignal)[]) => { - updateListeners.forEach(unsub => unsub()) - updateListeners = [] - sources.forEach(source => { - const unsub = observeValueUpdate( - source, - value => signalUpdates.push({ ...getNodeState(source), value }), - SYMBOL, - ) - updateListeners.push(unsub) - }) - } - - // this is for logging the initial state after the first callback execution - // the "value" property is monkey patched for one function execution - if (initialRun) { - const removeValueObserver = observeValueUpdate( - owner, - value => { - const timeElapsed = time() - removeValueObserver() - const sources = owner.sources ? dedupeArray(owner.sources) : [] - - logComputation(getComputationCreatedLabel(typeName, name, timeElapsed), { - owner: { type, typeName, name }, - owned: owner.owned ?? [], - sources, - prev: UNUSED, - value: usesValue ? value : UNUSED, - causedBy: null, - }) - observeSources(sources) - }, - SYMBOL, - ) - } - // if debugComputation after the initial run of the computation - // sources should be observed immediately - else observeSources(owner.sources ? dedupeArray(owner.sources) : []) - - // monkey patch the "fn" callback to intercept every computation function execution - interceptComputationRerun(owner, (fn, prev) => { - const updates = signalUpdates - signalUpdates = [] - - time() - const value = fn() - const elapsedTime = time() - - const sources = owner.sources ? dedupeArray(owner.sources) : [] - logComputation(getComputationRerunLabel(name, elapsedTime), { - owner: UNUSED, - owned: owner.owned ?? [], - sources, - prev: usesPrev ? prev : UNUSED, - value: usesValue ? value : UNUSED, - causedBy: updates, - }) - observeSources(sources) - }) - - // CLEANUP - // listen to parent cleanup, instead of own, because for computations onCleanup runs for every re-execution - onParentCleanup( - owner, - () => { - console.log(...getOwnerDisposedLabel(name)) - updateListeners.forEach(unsub => unsub()) - updateListeners.length = 0 - signalUpdates.length = 0 - }, - // run before other cleanup functions - true, - ) - - const time = makeTimeMeter() + const owner = _owner === undefined ? getOwner() : (_owner as SolidOwner) + if (!owner || !isSolidComputation(owner)) return console.warn("owner is not a computation") + + if (markDebugNode(owner, "computation") === true) return + + const { type, typeName, name } = getNodeState(owner) + const SYMBOL = Symbol(name) + // log prev value only of the computation callback uses it + const usesPrev = !!owner.fn.length + const usesValue = usesPrev || type === NodeType.Memo + + let updateListeners: VoidFunction[] = [] + let signalUpdates: NodeStateWithValue[] = [] + + // patches source objects to track their value updates + // will unsubscribe from previous sources on each call + const observeSources = (sources: (SolidComputation | SolidSignal)[]) => { + updateListeners.forEach(unsub => unsub()) + updateListeners = [] + sources.forEach(source => { + const unsub = observeValueUpdate( + source, + value => signalUpdates.push({ ...getNodeState(source), value }), + SYMBOL, + ) + updateListeners.push(unsub) + }) + } + + // this is for logging the initial state after the first callback execution + // the "value" property is monkey patched for one function execution + if (initialRun) { + const removeValueObserver = observeValueUpdate( + owner, + value => { + const timeElapsed = time() + removeValueObserver() + const sources = owner.sources ? dedupeArray(owner.sources) : [] + + logComputation(getComputationCreatedLabel(typeName, name, timeElapsed), { + owner: { type, typeName, name }, + owned: owner.owned ?? [], + sources, + prev: UNUSED, + value: usesValue ? value : UNUSED, + causedBy: null, + }) + observeSources(sources) + }, + SYMBOL, + ) + } + // if debugComputation after the initial run of the computation + // sources should be observed immediately + else observeSources(owner.sources ? dedupeArray(owner.sources) : []) + + // monkey patch the "fn" callback to intercept every computation function execution + interceptComputationRerun(owner, (fn, prev) => { + const updates = signalUpdates + signalUpdates = [] + + time() + const value = fn() + const elapsedTime = time() + + const sources = owner.sources ? dedupeArray(owner.sources) : [] + logComputation(getComputationRerunLabel(name, elapsedTime), { + owner: UNUSED, + owned: owner.owned ?? [], + sources, + prev: usesPrev ? prev : UNUSED, + value: usesValue ? value : UNUSED, + causedBy: updates, + }) + observeSources(sources) + }) + + // CLEANUP + // listen to parent cleanup, instead of own, because for computations onCleanup runs for every re-execution + onParentCleanup( + owner, + () => { + console.log(...getOwnerDisposedLabel(name)) + updateListeners.forEach(unsub => unsub()) + updateListeners.length = 0 + signalUpdates.length = 0 + }, + // run before other cleanup functions + true, + ) + + const time = makeTimeMeter() } /** @@ -212,47 +211,47 @@ export function debugComputation( */ export function debugOwnerComputations(owner?: Owner): void export function debugOwnerComputations(_owner?: Owner): void { - const owner = _owner === undefined ? getOwner() : (_owner as SolidOwner) - if (!owner) return console.warn("no owner passed to debugOwnedComputations") - - const marked = markDebugNode(owner, "owned") - if (marked === true) return - onCleanup(marked) - - // for solid-refresh HMR memos, return the owned component - const { type, typeName, name } = getNodeState( - lookupOwner(owner, o => getOwnerType(o) !== NodeType.Refresh)!, - ) - - let prevOwned: SolidComputation[] = [] - - makeSolidUpdateListener(() => { - const { owned } = owner - if (!owned) return - - let computations: SolidComputation[] = [] - - let i = prevOwned.length - // owned can only be added - for (; i < owned.length; i++) { - const computation = owned[i] - debugComputation(computation, { - initialRun: false, - }) - computations.push(computation) - } - if (computations.length === 0) return - - computations = [...prevOwned, ...computations] - // log owned computation changes - logOwned({ type, typeName, name }, computations, prevOwned) - prevOwned = computations - }) + const owner = _owner === undefined ? getOwner() : (_owner as SolidOwner) + if (!owner) return console.warn("no owner passed to debugOwnedComputations") + + const marked = markDebugNode(owner, "owned") + if (marked === true) return + onCleanup(marked) + + // for solid-refresh HMR memos, return the owned component + const { type, typeName, name } = getNodeState( + lookupOwner(owner, o => getOwnerType(o) !== NodeType.Refresh)!, + ) + + let prevOwned: SolidComputation[] = [] + + makeSolidUpdateListener(() => { + const { owned } = owner + if (!owned) return + + let computations: SolidComputation[] = [] + + let i = prevOwned.length + // owned can only be added + for (; i < owned.length; i++) { + const computation = owned[i] + debugComputation(computation, { + initialRun: false, + }) + computations.push(computation) + } + if (computations.length === 0) return + + computations = [...prevOwned, ...computations] + // log owned computation changes + logOwned({ type, typeName, name }, computations, prevOwned) + prevOwned = computations + }) } export interface DebugSignalOptions { - trackObservers?: boolean - logInitialValue?: boolean + trackObservers?: boolean + logInitialValue?: boolean } /** @@ -271,74 +270,74 @@ export interface DebugSignalOptions { * ``` */ export function debugSignal( - source: Accessor | SignalState, - options: DebugSignalOptions = {}, + source: Accessor | SignalState, + options: DebugSignalOptions = {}, ): void { - let signal: SolidSignal - - if (typeof source === "function") { - const sources = getFunctionSources(source) - if (sources.length === 0) return console.warn("No signal was passed to debugSignal") - else if (sources.length > 1) - return console.warn("More then one signal was passed to debugSignal") - signal = sources[0] - } else { - signal = source as SolidSignal - } - - if (markDebugNode(signal) === true) return - - const { trackObservers = true, logInitialValue: _logInitialValue = true } = options - - const state = getNodeState(signal) - const SYMBOL = Symbol(state.name) - - // Initial - _logInitialValue && logInitialValue({ ...state, value: signal.value }) - - let actualObservers: SolidComputation[] - let prevObservers: SolidComputation[] = [] - let actualPrevObservers: SolidComputation[] = [] - - if (!signal.observers) { - signal.observers = [] - signal.observerSlots = [] - } - - // Value Update - const stopListening = observeValueUpdate( - signal, - (value, prev) => { - logSignalValueUpdate( - state, - value, - prev, - trackObservers ? prevObservers : dedupeArray(signal.observers!), - ) - }, - SYMBOL, - ) - if (getOwner()) onCleanup(stopListening) - - if (trackObservers) { - // Observers Change - function logObserversChange() { - const observers = dedupeArray(actualObservers) - if (arrayRefEquals(observers, prevObservers)) return - logObservers(state.name, observers, prevObservers) - prevObservers = [...observers] - actualPrevObservers = [...actualObservers] - } - - // Listen to Solid's _$afterUpdate hook to check if observers changed - makeSolidUpdateListener(() => { - actualObservers = signal.observers! - if (actualObservers.length !== actualPrevObservers.length) return logObserversChange() - for (let i = actualObservers.length; i >= 0; i--) { - if (actualObservers[i] !== actualPrevObservers[i]) return logObserversChange() - } - }) - } + let signal: SolidSignal + + if (typeof source === "function") { + const sources = getFunctionSources(source) + if (sources.length === 0) return console.warn("No signal was passed to debugSignal") + else if (sources.length > 1) + return console.warn("More then one signal was passed to debugSignal") + signal = sources[0] + } else { + signal = source as SolidSignal + } + + if (markDebugNode(signal) === true) return + + const { trackObservers = true, logInitialValue: _logInitialValue = true } = options + + const state = getNodeState(signal) + const SYMBOL = Symbol(state.name) + + // Initial + _logInitialValue && logInitialValue({ ...state, value: signal.value }) + + let actualObservers: SolidComputation[] + let prevObservers: SolidComputation[] = [] + let actualPrevObservers: SolidComputation[] = [] + + if (!signal.observers) { + signal.observers = [] + signal.observerSlots = [] + } + + // Value Update + const stopListening = observeValueUpdate( + signal, + (value, prev) => { + logSignalValueUpdate( + state, + value, + prev, + trackObservers ? prevObservers : dedupeArray(signal.observers!), + ) + }, + SYMBOL, + ) + if (getOwner()) onCleanup(stopListening) + + if (trackObservers) { + // Observers Change + function logObserversChange() { + const observers = dedupeArray(actualObservers) + if (arrayRefEquals(observers, prevObservers)) return + logObservers(state.name, observers, prevObservers) + prevObservers = [...observers] + actualPrevObservers = [...actualObservers] + } + + // Listen to Solid's _$afterUpdate hook to check if observers changed + makeSolidUpdateListener(() => { + actualObservers = signal.observers! + if (actualObservers.length !== actualPrevObservers.length) return logObserversChange() + for (let i = actualObservers.length; i >= 0; i--) { + if (actualObservers[i] !== actualPrevObservers[i]) return logObserversChange() + } + }) + } } /** @@ -358,31 +357,31 @@ export function debugSignal( * ``` */ export function debugSignals( - source: Many> | SignalState[], - options: DebugSignalOptions = {}, + source: Many> | SignalState[], + options: DebugSignalOptions = {}, ): void { - let signals: SolidSignal[] = [] - asArray(source).forEach(s => { - if (typeof s === "function") signals.push.apply(signals, getFunctionSources(s)) - else signals.push(s as SolidSignal) - }) - if (signals.length === 0) return console.warn("No signals were passed to debugSignals") + let signals: SolidSignal[] = [] + asArray(source).forEach(s => { + if (typeof s === "function") signals.push.apply(signals, getFunctionSources(s)) + else signals.push(s as SolidSignal) + }) + if (signals.length === 0) return console.warn("No signals were passed to debugSignals") - // filter out already debugged signals - signals = signals.filter(s => !s.$debugSignal) + // filter out already debugged signals + signals = signals.filter(s => !s.$debugSignal) - if (signals.length === 1) return debugSignal(signals[0], options) + if (signals.length === 1) return debugSignal(signals[0], options) - const { logInitialValue = true } = options + const { logInitialValue = true } = options - if (logInitialValue) logSignalsInitialValues(signals) + if (logInitialValue) logSignalsInitialValues(signals) - signals.forEach(signal => { - debugSignal(signal, { - ...options, - logInitialValue: false, - }) - }) + signals.forEach(signal => { + debugSignal(signal, { + ...options, + logInitialValue: false, + }) + }) } /** @@ -405,45 +404,45 @@ export function debugSignals( * ``` */ export function debugOwnerSignals(owner?: Owner, options: DebugSignalOptions = {}) { - owner = getOwner()! - if (!owner) return console.warn("debugOwnerState found no Owner") - - if (markDebugNode(owner, "signals") === true) return - - const solidOwner = owner as SolidOwner - - let prevSourceListLength = 0 - let prevOwnedLength = 0 - - makeSolidUpdateListener(() => { - const signals: SolidSignal[] = [] - - let i: number - // add owned signals - if (solidOwner.sourceMap) { - const sourceList = Object.values(solidOwner.sourceMap) - // signals can only be added - for (i = prevSourceListLength; i < sourceList.length; i++) signals.push(sourceList[i]) - prevSourceListLength = i - } - // add owned memos - if (solidOwner.owned) { - // owned can only be added - for (i = prevOwnedLength; i < solidOwner.owned.length; i++) { - const owner = solidOwner.owned[i] - if (isSolidMemo(owner)) signals.push(owner) - } - prevOwnedLength = i - } - - if (signals.length === 0) return - - debugSignals(signals, options) - }) + owner = getOwner()! + if (!owner) return console.warn("debugOwnerState found no Owner") + + if (markDebugNode(owner, "signals") === true) return + + const solidOwner = owner as SolidOwner + + let prevSourceListLength = 0 + let prevOwnedLength = 0 + + makeSolidUpdateListener(() => { + const signals: SolidSignal[] = [] + + let i: number + // add owned signals + if (solidOwner.sourceMap) { + const sourceList = Object.values(solidOwner.sourceMap) + // signals can only be added + for (i = prevSourceListLength; i < sourceList.length; i++) signals.push(sourceList[i]) + prevSourceListLength = i + } + // add owned memos + if (solidOwner.owned) { + // owned can only be added + for (i = prevOwnedLength; i < solidOwner.owned.length; i++) { + const owner = solidOwner.owned[i] + if (isSolidMemo(owner)) signals.push(owner) + } + prevOwnedLength = i + } + + if (signals.length === 0) return + + debugSignals(signals, options) + }) } const getPropValue = (props: Record, desc: PropertyDescriptor): unknown => - untrack(() => (desc.get ? desc.get.call(props) : desc.value)) + untrack(() => (desc.get ? desc.get.call(props) : desc.value)) /** * Debug the provided {@link props} object by logging their state to the console. @@ -457,109 +456,109 @@ const getPropValue = (props: Record, desc: PropertyDescriptor): * ``` */ export function debugProps(props: Record): void { - const owner = getOwner() - if (!owner) return console.warn("debugProps should be used synchronously inside a component") - - // for solid-refresh HMR memos, return the owned component - const ownerState = getNodeState(lookupOwner(owner, o => getOwnerType(o) !== NodeType.Refresh)!) - const isProxy = isSolidProxy(props) - - const descriptorsList = Object.entries(Object.getOwnPropertyDescriptors(props)) - - if (descriptorsList.length === 0) console.log(...getPropsInitLabel(ownerState, isProxy, true)) - else { - console.groupCollapsed(...getPropsInitLabel(ownerState, isProxy, false)) - paddedForEach( - descriptorsList, - ([, desc]) => (desc.get ? "Getter" : "Value"), - (type, [key, desc]) => { - const value = getPropValue(props, desc) - const signals = type === "Getter" ? getFunctionSources(() => props[key]) : [] - const label = getPropLabel(type, key, value, null) - - if (signals.length > 0) { - console.groupCollapsed(...label) - signals.forEach(logSignalValue) - console.groupEnd() - } else console.log(...label) - }, - ) - console.groupEnd() - } - - if (isProxy) { - createEffect( - on( - () => Object.keys(props), - (keys, prevKeys) => { - if (!prevKeys) return - if (arrayEquals(keys, prevKeys)) return - - const descriptors = Object.getOwnPropertyDescriptors(props) - - if (Object.entries(descriptors).length === 0) { - console.log(...getPropsKeyUpdateLabel(ownerState, true)) - } else { - const [getMark, allKeys] = getDiffMap(prevKeys, keys, Map) - console.groupCollapsed(...getPropsKeyUpdateLabel(ownerState, false)) - allKeys.forEach(key => { - const mark = getMark(key) - - if (mark === "removed") - return console.log(...getPropLabel("Getter", key, null, "removed")) - - const desc = descriptors[key] - const value = getPropValue(props, desc) - const label = getPropLabel("Getter", key, value, mark) - const signals = getFunctionSources(() => props[key]) - - if (signals.length > 0) { - console.groupCollapsed(...label) - signals.forEach(logSignalValue) - console.groupEnd() - } else console.log(...label) - }) - console.groupEnd() - } - }, - ), - undefined, - { name: "debugProps EFFECT" }, - ) - } + const owner = getOwner() + if (!owner) return console.warn("debugProps should be used synchronously inside a component") + + // for solid-refresh HMR memos, return the owned component + const ownerState = getNodeState(lookupOwner(owner, o => getOwnerType(o) !== NodeType.Refresh)!) + const isProxy = isSolidProxy(props) + + const descriptorsList = Object.entries(Object.getOwnPropertyDescriptors(props)) + + if (descriptorsList.length === 0) console.log(...getPropsInitLabel(ownerState, isProxy, true)) + else { + console.groupCollapsed(...getPropsInitLabel(ownerState, isProxy, false)) + paddedForEach( + descriptorsList, + ([, desc]) => (desc.get ? "Getter" : "Value"), + (type, [key, desc]) => { + const value = getPropValue(props, desc) + const signals = type === "Getter" ? getFunctionSources(() => props[key]) : [] + const label = getPropLabel(type, key, value, null) + + if (signals.length > 0) { + console.groupCollapsed(...label) + signals.forEach(logSignalValue) + console.groupEnd() + } else console.log(...label) + }, + ) + console.groupEnd() + } + + if (isProxy) { + createEffect( + on( + () => Object.keys(props), + (keys, prevKeys) => { + if (!prevKeys) return + if (arrayEquals(keys, prevKeys)) return + + const descriptors = Object.getOwnPropertyDescriptors(props) + + if (Object.entries(descriptors).length === 0) { + console.log(...getPropsKeyUpdateLabel(ownerState, true)) + } else { + const [getMark, allKeys] = getDiffMap(prevKeys, keys, Map) + console.groupCollapsed(...getPropsKeyUpdateLabel(ownerState, false)) + allKeys.forEach(key => { + const mark = getMark(key) + + if (mark === "removed") + return console.log(...getPropLabel("Getter", key, null, "removed")) + + const desc = descriptors[key] + const value = getPropValue(props, desc) + const label = getPropLabel("Getter", key, value, mark) + const signals = getFunctionSources(() => props[key]) + + if (signals.length > 0) { + console.groupCollapsed(...label) + signals.forEach(logSignalValue) + console.groupEnd() + } else console.log(...label) + }) + console.groupEnd() + } + }, + ), + undefined, + { name: "debugProps EFFECT" }, + ) + } } -export function debugStore(store: object): void { - if (!isSolidProxy(store)) { - console.warn("debugStore should be used with a proxy. Instead used on:", store) - return - } - const owner = getOwner() - if (!owner) { - console.warn("debugStore should be used synchronously inside an owner") - return - } - const argTarget = (store as any)[$RAW] - // console.log(argTarget) - - if (owner.sourceMap) { - // Object.defineProperty(argTarget, $RAW, { - // get() { - // console.log("ASDFGSDGDRHDZ") - // return argTarget - // }, - // }) - // const ownerTarget = Object.values(owner.sourceMap).find( - // s => !("name" in s) && s.value === argTarget, - // ) - // if (ownerTarget) { - // ownerTarget.value = { - // 0: { - // title: "Ahahaha", - // done: true, - // }, - // } - // console.log(ownerTarget) - // } - } -} +// export function debugStore(store: object): void { +// if (!isSolidProxy(store)) { +// console.warn("debugStore should be used with a proxy. Instead used on:", store) +// return +// } +// const owner = getOwner() +// if (!owner) { +// console.warn("debugStore should be used synchronously inside an owner") +// return +// } +// const argTarget = (store as any)[$RAW] +// // console.log(argTarget) + +// if (owner.sourceMap) { +// // Object.defineProperty(argTarget, $RAW, { +// // get() { +// // console.log("ASDFGSDGDRHDZ") +// // return argTarget +// // }, +// // }) +// // const ownerTarget = Object.values(owner.sourceMap).find( +// // s => !("name" in s) && s.value === argTarget, +// // ) +// // if (ownerTarget) { +// // ownerTarget.value = { +// // 0: { +// // title: "Ahahaha", +// // done: true, +// // }, +// // } +// // console.log(ownerTarget) +// // } +// } +// } From 7721110361b96eb7fb9c77d52a5c9cd0eb1c3641 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 15:51:00 +0200 Subject: [PATCH 07/13] rename `"babel-plugin"` package to `"transform"` --- .changeset/happy-falcons-smash.md | 5 + README.md | 8 +- packages/babel-plugin/README.md | 39 ---- packages/babel-plugin/src/index.ts | 60 ------- packages/locator/README.md | 8 +- packages/locator/package.json | 152 ++++++++-------- packages/main/CHANGELOG.md | 6 +- packages/main/README.md | 6 +- packages/main/package.json | 166 +++++++++--------- packages/main/src/vite.ts | 2 +- .../{babel-plugin => transform}/CHANGELOG.md | 4 +- packages/transform/README.md | 58 ++++++ .../jest.config.ts | 0 .../{babel-plugin => transform}/package.json | 4 +- packages/transform/src/index.ts | 64 +++++++ .../src/jsxLocation.ts | 0 .../{babel-plugin => transform}/src/utils.ts | 0 .../src/wrapStores.ts | 0 .../test/wrapStores.test.ts | 0 .../{babel-plugin => transform}/tsconfig.json | 0 .../tsup.config.ts | 0 packages/ui/package.json | 98 +++++------ playgrounds/sandbox/package.json | 48 ++--- playgrounds/start/package.json | 52 +++--- playgrounds/start/vite.config.ts | 4 +- pnpm-lock.yaml | 110 ++++++------ 26 files changed, 462 insertions(+), 432 deletions(-) create mode 100644 .changeset/happy-falcons-smash.md delete mode 100644 packages/babel-plugin/README.md delete mode 100644 packages/babel-plugin/src/index.ts rename packages/{babel-plugin => transform}/CHANGELOG.md (84%) create mode 100644 packages/transform/README.md rename packages/{babel-plugin => transform}/jest.config.ts (100%) rename packages/{babel-plugin => transform}/package.json (94%) create mode 100644 packages/transform/src/index.ts rename packages/{babel-plugin => transform}/src/jsxLocation.ts (100%) rename packages/{babel-plugin => transform}/src/utils.ts (100%) rename packages/{babel-plugin => transform}/src/wrapStores.ts (100%) rename packages/{babel-plugin => transform}/test/wrapStores.test.ts (100%) rename packages/{babel-plugin => transform}/tsconfig.json (100%) rename packages/{babel-plugin => transform}/tsup.config.ts (100%) diff --git a/.changeset/happy-falcons-smash.md b/.changeset/happy-falcons-smash.md new file mode 100644 index 00000000..2d2a08fa --- /dev/null +++ b/.changeset/happy-falcons-smash.md @@ -0,0 +1,5 @@ +--- +"@solid-devtools/transform": minor +--- + +Add wrapStores transform. diff --git a/README.md b/README.md index e84e8ed0..b3e4020f 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,11 @@ For debugging only the pinpoint places parts of the Solid's reactivity graph you Provides a variaty of debugging utilities for logging the state and lifecycle of the nodes of reactivity graph to the browser console. -### [Babel Plugin](./packages/babel-plugin/) +### [Transform](./packages/transform/) -###### `@solid-devtools/babel-plugin` +###### `@solid-devtools/transform` -A babel plugin for vite for transforming Solid code. For development — debugging purposes only. - -Currently only transforms JSX, adding code location to it. Necessary for the [Locator](./packages/locator#readme) package. +A babel transform plugin for vite for transforming Solid code. For development — debugging purposes only. ### [Chrome Extension](./packages/extension#readme) diff --git a/packages/babel-plugin/README.md b/packages/babel-plugin/README.md deleted file mode 100644 index 29425117..00000000 --- a/packages/babel-plugin/README.md +++ /dev/null @@ -1,39 +0,0 @@ - -

- Solid Devtools Babel Plugin -

-
- -# @solid-devtools/babel-plugin - -Babel plugin for transforming SolidJS code in development to enchance solid-devtools usage. - -## Getting Started - -### Installation - -```bash -npm i @solid-devtools/babel-plugin -# or -yarn add @solid-devtools/babel-plugin -# or -pnpm i @solid-devtools/babel-plugin -``` - -### Setup - -```ts -// vite.config.ts - -import { defineConfig } from "vite" -import solidPlugin from "vite-plugin-solid" -import { devtoolsPlugin } from "@solid-devtools/babel-plugin" - -export default defineConfig({ - plugins: [devtoolsPlugin(), solidPlugin()], -}) -``` - -## Changelog - -See [CHANGELOG.md](./CHANGELOG.md). diff --git a/packages/babel-plugin/src/index.ts b/packages/babel-plugin/src/index.ts deleted file mode 100644 index afdf19f5..00000000 --- a/packages/babel-plugin/src/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { PluginItem, transformAsync } from "@babel/core" -import { PluginOption } from "vite" -import { getFileExtension } from "./utils" -import jsxLocationPlugin from "./jsxLocation" -import wrapStoresPlugin from "./wrapStores" - -export interface DevtoolsPluginOptions { - wrapStores?: boolean - jsxLocation?: boolean -} - -// This export is used for configuration. -export const devtoolsPlugin = (options: DevtoolsPluginOptions = {}): PluginOption => { - const { wrapStores = false, jsxLocation = false } = options - - let enablePlugin = false - let projectRoot = process.cwd() - - return { - name: "solid-devtools", - enforce: "pre", - configResolved(config) { - enablePlugin = config.command === "serve" && config.mode !== "production" - }, - async transform(source, id, transformOptions) { - // production and server should be disabled - if (transformOptions?.ssr || !enablePlugin) return - - const extension = getFileExtension(id) - - if (!["js", "jsx", "ts", "tsx"].includes(extension)) return - - const isJSX = extension === "jsx" || extension === "tsx" - const plugins: PluginItem[] = [] - - // plugins that should only run on .tsx/.jsx files in development - if (jsxLocation && isJSX) plugins.push(jsxLocationPlugin) - if (wrapStores) plugins.push(wrapStoresPlugin) - - if (plugins.length === 0) return - - // babel doesn't work with typescript by default - plugins.splice(0, 0, ["@babel/plugin-syntax-typescript", { isTSX: isJSX }]) - - const result = await transformAsync(source, { - babelrc: false, - configFile: false, - root: projectRoot, - filename: id, - sourceFileName: id, - plugins, - }) - - if (!result) return null - const { code } = result - if (!code) return null - return { code } - }, - } -} diff --git a/packages/locator/README.md b/packages/locator/README.md index c72f1cab..ab41b71d 100644 --- a/packages/locator/README.md +++ b/packages/locator/README.md @@ -34,7 +34,13 @@ import solidPlugin from "vite-plugin-solid" import devtoolsPlugin from "solid-devtools/vite" export default defineConfig({ - plugins: [devtoolsPlugin(), solidPlugin()], + plugins: [ + devtoolsPlugin({ + // enabling this option is required for the plugin to work + jsxLocation: true, + }), + solidPlugin(), + ], }) ``` diff --git a/packages/locator/package.json b/packages/locator/package.json index 3b7c0aae..7ca4dc69 100644 --- a/packages/locator/package.json +++ b/packages/locator/package.json @@ -1,78 +1,78 @@ { - "name": "@solid-devtools/locator", - "version": "0.8.1", - "description": "On-page overlay for localisating SolidJS components, and revealing them in your code editor.", - "license": "MIT", - "author": "Damian Tarnawski ", - "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/locator#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/thetarnav/solid-devtools.git" - }, - "bugs": { - "url": "https://github.com/thetarnav/solid-devtools/issues" - }, - "keywords": [ - "solid", - "devtools", - "components", - "locator", - "vscode" - ], - "private": false, - "sideEffects": false, - "type": "module", - "files": [ - "dist" - ], - "main": "./dist/server.cjs", - "module": "./dist/server.js", - "types": "./dist/index.d.ts", - "exports": { - "browser": { - "development": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "scripts": { - "dev": "tsup --watch", - "build": "tsup", - "test": "jest --config jest.config.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@testing-library/jest-dom": "^5.16.4", - "esbuild": "^0.14.51", - "esbuild-plugin-solid": "^0.4.2", - "jest": "^28.1.3", - "jest-environment-jsdom": "^28.1.3", - "solid-js": "^1.4.8", - "ts-node": "^10.9.1", - "tsup": "^6.2.1", - "typescript": "^4.7.4" - }, - "dependencies": { - "@solid-devtools/debugger": "^0.2.3", - "@solid-devtools/ui": "0.3.3", - "@solid-primitives/bounds": "^0.0.100", - "@solid-primitives/event-listener": "^2.2.1", - "@solid-primitives/keyboard": "^0.0.100", - "@solid-primitives/platform": "^0.0.100", - "@solid-primitives/utils": "^3.0.1", - "motion": "^10.13.1" - }, - "peerDependencies": { - "solid-js": "^1.4.4" - }, - "optionalDependencies": { - "@solid-devtools/babel-plugin": "^0.3.1" - }, - "packageManager": "pnpm@7.7.0" + "name": "@solid-devtools/locator", + "version": "0.8.1", + "description": "On-page overlay for localisating SolidJS components, and revealing them in your code editor.", + "license": "MIT", + "author": "Damian Tarnawski ", + "contributors": [], + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/locator#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thetarnav/solid-devtools.git" + }, + "bugs": { + "url": "https://github.com/thetarnav/solid-devtools/issues" + }, + "keywords": [ + "solid", + "devtools", + "components", + "locator", + "vscode" + ], + "private": false, + "sideEffects": false, + "type": "module", + "files": [ + "dist" + ], + "main": "./dist/server.cjs", + "module": "./dist/server.js", + "types": "./dist/index.d.ts", + "exports": { + "browser": { + "development": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "test": "jest --config jest.config.ts", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@testing-library/jest-dom": "^5.16.4", + "esbuild": "^0.14.51", + "esbuild-plugin-solid": "^0.4.2", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "solid-js": "^1.4.8", + "ts-node": "^10.9.1", + "tsup": "^6.2.1", + "typescript": "^4.7.4" + }, + "dependencies": { + "@solid-devtools/debugger": "^0.2.3", + "@solid-devtools/ui": "0.3.3", + "@solid-primitives/bounds": "^0.0.100", + "@solid-primitives/event-listener": "^2.2.1", + "@solid-primitives/keyboard": "^0.0.100", + "@solid-primitives/platform": "^0.0.100", + "@solid-primitives/utils": "^3.0.1", + "motion": "^10.13.1" + }, + "peerDependencies": { + "solid-js": "^1.4.4" + }, + "optionalDependencies": { + "@solid-devtools/transform": "^0.3.1" + }, + "packageManager": "pnpm@7.7.0" } diff --git a/packages/main/CHANGELOG.md b/packages/main/CHANGELOG.md index 89e75086..6ba4fc7a 100644 --- a/packages/main/CHANGELOG.md +++ b/packages/main/CHANGELOG.md @@ -6,7 +6,7 @@ - fdb09bc: Various minor changes. - Updated dependencies [fdb09bc] - - @solid-devtools/babel-plugin@0.3.1 + - @solid-devtools/transform@0.3.1 - @solid-devtools/debugger@0.2.3 - @solid-devtools/extension-adapter@0.8.1 - @solid-devtools/locator@0.8.1 @@ -20,7 +20,7 @@ ### Patch Changes - Updated dependencies [4c79a90] - - @solid-devtools/babel-plugin@0.3.0 + - @solid-devtools/transform@0.3.0 - @solid-devtools/locator@0.8.0 ## 0.7.2 @@ -29,7 +29,7 @@ - a8d0354: Correct "homepage" filed in package.json, to lead to individual package readme. - Updated dependencies [a8d0354] - - @solid-devtools/babel-plugin@0.2.1 + - @solid-devtools/transform@0.2.1 - @solid-devtools/debugger@0.2.2 - @solid-devtools/extension-adapter@0.7.2 - @solid-devtools/locator@0.7.2 diff --git a/packages/main/README.md b/packages/main/README.md index 69f8832f..5825294f 100644 --- a/packages/main/README.md +++ b/packages/main/README.md @@ -11,7 +11,7 @@ The main package of Solid Devtools. It contains the following subpackages: - [Debugger](https://github.com/thetarnav/solid-devtools/tree/main/packages/debugger#readme) - [Extension Adapter](https://github.com/thetarnav/solid-devtools/tree/main/packages/extension-adapter#readme) _(this one is automatically enabled)_ - [Locator](https://github.com/thetarnav/solid-devtools/tree/main/packages/locator#readme) — [How to use it](#using-the-locator-package) -- [Babel Plugin](https://github.com/thetarnav/solid-devtools/tree/main/packages/babel-plugin#readme) — [How to use it](#enabling-the-babel-plugin) +- [Babel Plugin](https://github.com/thetarnav/solid-devtools/tree/main/packages/transform#readme) — [How to use it](#enabling-the-babel-plugin) ## Getting started @@ -33,7 +33,7 @@ The `solid-devtools` package comes with the [Locator](https://github.com/thetarn ### Enabling the Babel plugin -`solid-devtools` reexports the [babel plugin](https://github.com/thetarnav/solid-devtools/tree/main/packages/babel-plugin#readme) as a vite plugin. +`solid-devtools` reexports the [babel plugin](https://github.com/thetarnav/solid-devtools/tree/main/packages/transform#readme) as a vite plugin. To enable it you need to add it to plugins array in your `.vite.config.js` file: @@ -49,6 +49,8 @@ export default defineConfig({ }) ``` +[**See transform options**](https://github.com/thetarnav/solid-devtools/tree/main/packages/transform#Options) + ## Changelog See [CHANGELOG.md](./CHANGELOG.md). diff --git a/packages/main/package.json b/packages/main/package.json index 7106ff89..e1c77201 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -1,85 +1,85 @@ { - "name": "solid-devtools", - "version": "0.8.1", - "description": "Runtime helpers for hooking up SolidJS application with Solid Devtools", - "license": "MIT", - "author": "Damian Tarnawski ", - "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/main#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/thetarnav/solid-devtools.git" - }, - "bugs": { - "url": "https://github.com/thetarnav/solid-devtools/issues" - }, - "keywords": [ - "solid", - "devtools", - "reactivity" - ], - "private": false, - "sideEffects": true, - "publishConfig": { - "access": "public" - }, - "files": [ - "dist" - ], - "type": "module", - "main": "./dist/server.cjs", - "module": "./dist/server.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "browser": { - "development": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs", - "types": "./dist/index.d.ts" - }, - "./vite": { - "import": "./dist/vite.js", - "require": "./dist/vite.cjs", - "types": "./dist/vite.d.ts" - } - }, - "typesVersions": { - "*": { - "vite": [ - "./dist/vite.d.ts" - ] - } - }, - "scripts": { - "dev": "tsup --watch", - "build": "tsup", - "test": "jest --config jest.config.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@testing-library/jest-dom": "^5.16.4", - "jest": "^28.1.3", - "jest-environment-jsdom": "^28.1.3", - "solid-js": "^1.4.8", - "ts-node": "^10.9.1", - "tsup": "^6.2.1", - "typescript": "^4.7.4" - }, - "dependencies": { - "@solid-devtools/babel-plugin": "^0.3.1", - "@solid-devtools/debugger": "^0.2.3", - "@solid-devtools/extension-adapter": "^0.8.1", - "@solid-devtools/locator": "^0.8.1" - }, - "peerDependencies": { - "solid-js": "^1.4.5" - }, - "packageManager": "pnpm@7.7.0" + "name": "solid-devtools", + "version": "0.8.1", + "description": "Runtime helpers for hooking up SolidJS application with Solid Devtools", + "license": "MIT", + "author": "Damian Tarnawski ", + "contributors": [], + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/main#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thetarnav/solid-devtools.git" + }, + "bugs": { + "url": "https://github.com/thetarnav/solid-devtools/issues" + }, + "keywords": [ + "solid", + "devtools", + "reactivity" + ], + "private": false, + "sideEffects": true, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/server.cjs", + "module": "./dist/server.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "browser": { + "development": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs", + "types": "./dist/index.d.ts" + }, + "./vite": { + "import": "./dist/vite.js", + "require": "./dist/vite.cjs", + "types": "./dist/vite.d.ts" + } + }, + "typesVersions": { + "*": { + "vite": [ + "./dist/vite.d.ts" + ] + } + }, + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "test": "jest --config jest.config.ts", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@testing-library/jest-dom": "^5.16.4", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "solid-js": "^1.4.8", + "ts-node": "^10.9.1", + "tsup": "^6.2.1", + "typescript": "^4.7.4" + }, + "dependencies": { + "@solid-devtools/transform": "^0.3.1", + "@solid-devtools/debugger": "^0.2.3", + "@solid-devtools/extension-adapter": "^0.8.1", + "@solid-devtools/locator": "^0.8.1" + }, + "peerDependencies": { + "solid-js": "^1.4.5" + }, + "packageManager": "pnpm@7.7.0" } diff --git a/packages/main/src/vite.ts b/packages/main/src/vite.ts index e174b1fa..bde95f47 100644 --- a/packages/main/src/vite.ts +++ b/packages/main/src/vite.ts @@ -1 +1 @@ -export { devtoolsPlugin as default } from "@solid-devtools/babel-plugin" +export * from "@solid-devtools/transform" diff --git a/packages/babel-plugin/CHANGELOG.md b/packages/transform/CHANGELOG.md similarity index 84% rename from packages/babel-plugin/CHANGELOG.md rename to packages/transform/CHANGELOG.md index b6f7668e..ecf365f6 100644 --- a/packages/babel-plugin/CHANGELOG.md +++ b/packages/transform/CHANGELOG.md @@ -1,4 +1,4 @@ -# @solid-devtools/babel-plugin +# @solid-devtools/transform ## 0.3.1 @@ -10,7 +10,7 @@ ### Minor Changes -- 4c79a90: Bump vite peerDepenedency to version 3.0.0 (#34) +- 4c79a90: Bump vite peerDependency to version 3.0.0 (#34) ## 0.2.1 diff --git a/packages/transform/README.md b/packages/transform/README.md new file mode 100644 index 00000000..10c94405 --- /dev/null +++ b/packages/transform/README.md @@ -0,0 +1,58 @@ + +

+ Solid Devtools Transform +

+
+ +# @solid-devtools/transform + +Vite plugin for transforming SolidJS code in development to enchance solid-devtools usage. + +## Getting Started + +### Installation + +```bash +npm i @solid-devtools/transform +# or +yarn add @solid-devtools/transform +# or +pnpm i @solid-devtools/transform +``` + +### Setup + +```ts +// vite.config.ts + +import { defineConfig } from "vite" +import solidPlugin from "vite-plugin-solid" +import devtoolsPlugin from "@solid-devtools/transform" + +export default defineConfig({ + plugins: [devtoolsPlugin(), solidPlugin()], +}) +``` + +### Options + +All of the transforms are disabled by default—you need to pick what you want by enabling correlated option. + +```ts +interface DevtoolsPluginOptions { + /** Wrap store creation to observe changes */ + wrapStores?: boolean + /** Inject location attributes to jsx templates */ + jsxLocation?: boolean +} + +// in vite.config.ts plugins array: +devtoolsPlugin({ + wrapStores: true, + jsxLocation: true, +}) +``` + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md). diff --git a/packages/babel-plugin/jest.config.ts b/packages/transform/jest.config.ts similarity index 100% rename from packages/babel-plugin/jest.config.ts rename to packages/transform/jest.config.ts diff --git a/packages/babel-plugin/package.json b/packages/transform/package.json similarity index 94% rename from packages/babel-plugin/package.json rename to packages/transform/package.json index b43d4dc0..dc7a80be 100644 --- a/packages/babel-plugin/package.json +++ b/packages/transform/package.json @@ -1,11 +1,11 @@ { - "name": "@solid-devtools/babel-plugin", + "name": "@solid-devtools/transform", "version": "0.3.1", "description": "Babel plugin for transforming SolidJS code in development to enchance solid-devtools usage.", "license": "MIT", "author": "Damian Tarnawski ", "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/babel-plugin#readme", + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/transform#readme", "repository": { "type": "git", "url": "git+https://github.com/thetarnav/solid-devtools.git" diff --git a/packages/transform/src/index.ts b/packages/transform/src/index.ts new file mode 100644 index 00000000..c94ec4d3 --- /dev/null +++ b/packages/transform/src/index.ts @@ -0,0 +1,64 @@ +import { PluginItem, transformAsync } from "@babel/core" +import { PluginOption } from "vite" +import { getFileExtension } from "./utils" +import jsxLocationPlugin from "./jsxLocation" +import wrapStoresPlugin from "./wrapStores" + +export interface DevtoolsPluginOptions { + /** Wrap store creation to observe changes */ + wrapStores?: boolean + /** Inject location attributes to jsx templates */ + jsxLocation?: boolean +} + +// This export is used for configuration. +const devtoolsPlugin = (options: DevtoolsPluginOptions = {}): PluginOption => { + const { wrapStores = false, jsxLocation = false } = options + + let enablePlugin = false + let projectRoot = process.cwd() + + return { + name: "solid-devtools", + enforce: "pre", + configResolved(config) { + enablePlugin = config.command === "serve" && config.mode !== "production" + }, + async transform(source, id, transformOptions) { + // production and server should be disabled + if (transformOptions?.ssr || !enablePlugin) return + + const extension = getFileExtension(id) + + if (!["js", "jsx", "ts", "tsx"].includes(extension)) return + + const isJSX = extension === "jsx" || extension === "tsx" + const plugins: PluginItem[] = [] + + // plugins that should only run on .tsx/.jsx files in development + if (jsxLocation && isJSX) plugins.push(jsxLocationPlugin) + if (wrapStores) plugins.push(wrapStoresPlugin) + + if (plugins.length === 0) return + + // babel doesn't work with typescript by default + plugins.splice(0, 0, ["@babel/plugin-syntax-typescript", { isTSX: isJSX }]) + + const result = await transformAsync(source, { + babelrc: false, + configFile: false, + root: projectRoot, + filename: id, + sourceFileName: id, + plugins, + }) + + if (!result) return null + const { code } = result + if (!code) return null + return { code } + }, + } +} + +export default devtoolsPlugin diff --git a/packages/babel-plugin/src/jsxLocation.ts b/packages/transform/src/jsxLocation.ts similarity index 100% rename from packages/babel-plugin/src/jsxLocation.ts rename to packages/transform/src/jsxLocation.ts diff --git a/packages/babel-plugin/src/utils.ts b/packages/transform/src/utils.ts similarity index 100% rename from packages/babel-plugin/src/utils.ts rename to packages/transform/src/utils.ts diff --git a/packages/babel-plugin/src/wrapStores.ts b/packages/transform/src/wrapStores.ts similarity index 100% rename from packages/babel-plugin/src/wrapStores.ts rename to packages/transform/src/wrapStores.ts diff --git a/packages/babel-plugin/test/wrapStores.test.ts b/packages/transform/test/wrapStores.test.ts similarity index 100% rename from packages/babel-plugin/test/wrapStores.test.ts rename to packages/transform/test/wrapStores.test.ts diff --git a/packages/babel-plugin/tsconfig.json b/packages/transform/tsconfig.json similarity index 100% rename from packages/babel-plugin/tsconfig.json rename to packages/transform/tsconfig.json diff --git a/packages/babel-plugin/tsup.config.ts b/packages/transform/tsup.config.ts similarity index 100% rename from packages/babel-plugin/tsup.config.ts rename to packages/transform/tsup.config.ts diff --git a/packages/ui/package.json b/packages/ui/package.json index ef2033ed..aefae1fd 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,51 +1,51 @@ { - "name": "@solid-devtools/ui", - "version": "0.3.3", - "description": "UI Library for visualization of the reactive graph. Powering Solid Devtools.", - "license": "MIT", - "author": "Damian Tarnawski ", - "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/ui#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/thetarnav/solid-devtools.git" - }, - "bugs": { - "url": "https://github.com/thetarnav/solid-devtools/issues" - }, - "keywords": [ - "solid", - "devtools" - ], - "private": false, - "sideEffects": false, - "type": "module", - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "dev": "tsup --watch", - "build": "tsup", - "test": "echo \"NOOP TEST\"", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@solid-devtools/babel-plugin": "^0.3.1", - "esbuild": "^0.14.51", - "esbuild-plugin-solid": "^0.4.2", - "solid-js": "^1.4.8", - "tsup": "^6.2.1", - "typescript": "^4.7.4" - }, - "dependencies": { - "@otonashixav/solid-flip": "^0.10.5", - "@solid-aria/interactions": "^0.1.4", - "@solid-primitives/props": "^2.2.1", - "@solid-primitives/utils": "^3.0.1", - "twind": "^0.16.17" - }, - "peerDependencies": { - "solid-js": "^1.4.4" - }, - "packageManager": "pnpm@7.7.0" + "name": "@solid-devtools/ui", + "version": "0.3.3", + "description": "UI Library for visualization of the reactive graph. Powering Solid Devtools.", + "license": "MIT", + "author": "Damian Tarnawski ", + "contributors": [], + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/ui#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thetarnav/solid-devtools.git" + }, + "bugs": { + "url": "https://github.com/thetarnav/solid-devtools/issues" + }, + "keywords": [ + "solid", + "devtools" + ], + "private": false, + "sideEffects": false, + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "test": "echo \"NOOP TEST\"", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@solid-devtools/transform": "^0.3.1", + "esbuild": "^0.14.51", + "esbuild-plugin-solid": "^0.4.2", + "solid-js": "^1.4.8", + "tsup": "^6.2.1", + "typescript": "^4.7.4" + }, + "dependencies": { + "@otonashixav/solid-flip": "^0.10.5", + "@solid-aria/interactions": "^0.1.4", + "@solid-primitives/props": "^2.2.1", + "@solid-primitives/utils": "^3.0.1", + "twind": "^0.16.17" + }, + "peerDependencies": { + "solid-js": "^1.4.4" + }, + "packageManager": "pnpm@7.7.0" } diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 688ddefe..b071fd0b 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -1,26 +1,26 @@ { - "name": "solid-devtools-sandbox-playground", - "version": "0.0.1", - "private": true, - "description": "", - "scripts": { - "start": "vite", - "dev": "vite", - "build": "vite build", - "serve": "vite preview" - }, - "license": "MIT", - "devDependencies": { - "@solid-devtools/babel-plugin": "^0.3.1", - "typescript": "^4.7.4", - "vite": "^3.0.4", - "vite-plugin-solid": "^2.3.0" - }, - "dependencies": { - "@solid-devtools/logger": "^0.2.0", - "@solid-primitives/timer": "^1.3.1", - "object-observer": "^5.0.4", - "solid-devtools": "^0.8.1", - "solid-js": "^1.4.8" - } + "name": "solid-devtools-sandbox-playground", + "version": "0.0.1", + "private": true, + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "devDependencies": { + "@solid-devtools/transform": "^0.3.1", + "typescript": "^4.7.4", + "vite": "^3.0.4", + "vite-plugin-solid": "^2.3.0" + }, + "dependencies": { + "@solid-devtools/logger": "^0.2.0", + "@solid-primitives/timer": "^1.3.1", + "object-observer": "^5.0.4", + "solid-devtools": "^0.8.1", + "solid-js": "^1.4.8" + } } diff --git a/playgrounds/start/package.json b/playgrounds/start/package.json index ac201307..8e0ac1ff 100644 --- a/playgrounds/start/package.json +++ b/playgrounds/start/package.json @@ -1,28 +1,28 @@ { - "name": "solid-devtools-start-playground", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "solid-start dev", - "build": "solid-start build", - "start": "solid-start start" - }, - "type": "module", - "main": "./dist/index.js", - "devDependencies": { - "@solid-devtools/babel-plugin": "^0.3.1", - "@solid-devtools/logger": "^0.2.0", - "solid-app-router": "^0.4.2", - "solid-devtools": "^0.8.1", - "solid-js": "^1.4.8", - "solid-meta": "^0.27.5", - "solid-start": "next", - "solid-start-node": "next", - "typescript": "^4.7.4", - "undici": "^4.16.0", - "vite": "2.9.12" - }, - "engines": { - "node": ">=14" - } + "name": "solid-devtools-start-playground", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "solid-start dev", + "build": "solid-start build", + "start": "solid-start start" + }, + "type": "module", + "main": "./dist/index.js", + "devDependencies": { + "@solid-devtools/transform": "^0.3.1", + "@solid-devtools/logger": "^0.2.0", + "solid-app-router": "^0.4.2", + "solid-devtools": "^0.8.1", + "solid-js": "^1.4.8", + "solid-meta": "^0.27.5", + "solid-start": "next", + "solid-start-node": "next", + "typescript": "^4.7.4", + "undici": "^4.16.0", + "vite": "2.9.12" + }, + "engines": { + "node": ">=14" + } } diff --git a/playgrounds/start/vite.config.ts b/playgrounds/start/vite.config.ts index b949b77b..32d3b418 100644 --- a/playgrounds/start/vite.config.ts +++ b/playgrounds/start/vite.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "vite" import solid from "solid-start" -import { devtoolsPlugin } from "@solid-devtools/babel-plugin" +import { devtoolsPlugin } from "@solid-devtools/transform" export default defineConfig({ - plugins: [devtoolsPlugin(), solid()], + plugins: [devtoolsPlugin(), solid()], }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac375ecc..eb906f1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,41 +38,6 @@ importers: turbo: 1.4.0 typescript: 4.7.4 - packages/babel-plugin: - specifiers: - '@babel/core': ^7.18.10 - '@babel/generator': ^7.18.10 - '@babel/parser': ^7.18.10 - '@babel/plugin-syntax-jsx': ^7.18.6 - '@babel/plugin-syntax-typescript': ^7.18.6 - '@babel/traverse': ^7.18.10 - '@babel/types': ^7.18.10 - '@types/babel__core': ^7.1.19 - '@types/babel__generator': ^7.6.4 - '@types/babel__traverse': ^7.17.1 - jest: ^28.1.3 - solid-js: ^1.4.4 - tsup: ^6.2.1 - typescript: ^4.7.4 - vite: ^3.0.4 - dependencies: - '@babel/core': 7.18.10 - '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.18.10 - '@babel/types': 7.18.10 - solid-js: 1.4.5 - devDependencies: - '@babel/generator': 7.18.10 - '@babel/parser': 7.18.10 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.10 - '@babel/traverse': 7.18.10 - '@types/babel__core': 7.1.19 - '@types/babel__generator': 7.6.4 - '@types/babel__traverse': 7.17.1 - jest: 28.1.3 - tsup: 6.2.1_typescript@4.7.4 - typescript: 4.7.4 - vite: 3.0.4 - packages/debugger: specifiers: '@solid-primitives/event-bus': ^0.1.0 @@ -152,8 +117,8 @@ importers: packages/locator: specifiers: - '@solid-devtools/babel-plugin': ^0.3.1 '@solid-devtools/debugger': ^0.2.3 + '@solid-devtools/transform': ^0.3.1 '@solid-devtools/ui': 0.3.3 '@solid-primitives/bounds': ^0.0.100 '@solid-primitives/event-listener': ^2.2.1 @@ -180,7 +145,7 @@ importers: '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 motion: 10.13.1 optionalDependencies: - '@solid-devtools/babel-plugin': link:../babel-plugin + '@solid-devtools/transform': link:../transform devDependencies: '@testing-library/jest-dom': 5.16.4 esbuild: 0.14.51 @@ -217,10 +182,10 @@ importers: packages/main: specifiers: - '@solid-devtools/babel-plugin': ^0.3.1 '@solid-devtools/debugger': ^0.2.3 '@solid-devtools/extension-adapter': ^0.8.1 '@solid-devtools/locator': ^0.8.1 + '@solid-devtools/transform': ^0.3.1 '@testing-library/jest-dom': ^5.16.4 jest: ^28.1.3 jest-environment-jsdom: ^28.1.3 @@ -229,10 +194,10 @@ importers: tsup: ^6.2.1 typescript: ^4.7.4 dependencies: - '@solid-devtools/babel-plugin': link:../babel-plugin '@solid-devtools/debugger': link:../debugger '@solid-devtools/extension-adapter': link:../extension-adapter '@solid-devtools/locator': link:../locator + '@solid-devtools/transform': link:../transform devDependencies: '@testing-library/jest-dom': 5.16.4 jest: 28.1.3_g3ob4elhesv7uyo2jbxkqs7hje @@ -275,11 +240,46 @@ importers: '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 solid-js: 1.4.8 + packages/transform: + specifiers: + '@babel/core': ^7.18.10 + '@babel/generator': ^7.18.10 + '@babel/parser': ^7.18.10 + '@babel/plugin-syntax-jsx': ^7.18.6 + '@babel/plugin-syntax-typescript': ^7.18.6 + '@babel/traverse': ^7.18.10 + '@babel/types': ^7.18.10 + '@types/babel__core': ^7.1.19 + '@types/babel__generator': ^7.6.4 + '@types/babel__traverse': ^7.17.1 + jest: ^28.1.3 + solid-js: ^1.4.4 + tsup: ^6.2.1 + typescript: ^4.7.4 + vite: ^3.0.4 + dependencies: + '@babel/core': 7.18.10 + '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.18.10 + '@babel/types': 7.18.10 + devDependencies: + '@babel/generator': 7.18.10 + '@babel/parser': 7.18.10 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.10 + '@babel/traverse': 7.18.10 + '@types/babel__core': 7.1.19 + '@types/babel__generator': 7.6.4 + '@types/babel__traverse': 7.17.1 + jest: 28.1.3 + solid-js: 1.4.8 + tsup: 6.2.1_typescript@4.7.4 + typescript: 4.7.4 + vite: 3.0.4 + packages/ui: specifiers: '@otonashixav/solid-flip': ^0.10.5 '@solid-aria/interactions': ^0.1.4 - '@solid-devtools/babel-plugin': ^0.3.1 + '@solid-devtools/transform': ^0.3.1 '@solid-primitives/props': ^2.2.1 '@solid-primitives/utils': ^3.0.1 esbuild: ^0.14.51 @@ -295,7 +295,7 @@ importers: '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 twind: 0.16.17_typescript@4.7.4 devDependencies: - '@solid-devtools/babel-plugin': link:../babel-plugin + '@solid-devtools/transform': link:../transform esbuild: 0.14.51 esbuild-plugin-solid: 0.4.2_vt7ue5mhxyhjzwryplo2mkvvwq solid-js: 1.4.8 @@ -319,8 +319,8 @@ importers: playgrounds/sandbox: specifiers: - '@solid-devtools/babel-plugin': ^0.3.1 '@solid-devtools/logger': ^0.2.0 + '@solid-devtools/transform': ^0.3.1 '@solid-primitives/timer': ^1.3.1 object-observer: ^5.0.4 solid-devtools: ^0.8.1 @@ -335,15 +335,15 @@ importers: solid-devtools: link:../../packages/main solid-js: 1.4.8 devDependencies: - '@solid-devtools/babel-plugin': link:../../packages/babel-plugin + '@solid-devtools/transform': link:../../packages/transform typescript: 4.7.4 vite: 3.0.4 vite-plugin-solid: 2.3.0_solid-js@1.4.8+vite@3.0.4 playgrounds/start: specifiers: - '@solid-devtools/babel-plugin': ^0.3.1 '@solid-devtools/logger': ^0.2.0 + '@solid-devtools/transform': ^0.3.1 solid-app-router: ^0.4.2 solid-devtools: ^0.8.1 solid-js: ^1.4.8 @@ -354,8 +354,8 @@ importers: undici: ^4.16.0 vite: 2.9.12 devDependencies: - '@solid-devtools/babel-plugin': link:../../packages/babel-plugin '@solid-devtools/logger': 0.2.1_solid-js@1.4.8 + '@solid-devtools/transform': link:../../packages/transform solid-app-router: 0.4.2_solid-js@1.4.8 solid-devtools: link:../../packages/main solid-js: 1.4.8 @@ -953,11 +953,11 @@ packages: /@babel/helper-plugin-utils/7.18.6: resolution: {integrity: sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==} engines: {node: '>=6.9.0'} + dev: true /@babel/helper-plugin-utils/7.18.9: resolution: {integrity: sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-remap-async-to-generator/7.16.8: resolution: {integrity: sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==} @@ -1814,7 +1814,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.18.6: @@ -1824,7 +1824,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.10: @@ -1982,7 +1982,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.18.2: resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} @@ -1991,7 +1991,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.18.5: @@ -2001,7 +2001,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.5 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.18.6: @@ -2011,7 +2011,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-arrow-functions/7.17.12_@babel+core@7.18.2: @@ -5280,7 +5280,7 @@ packages: dev: true /concat-map/0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true /connect-injector/0.4.4: @@ -9350,10 +9350,6 @@ packages: solid-js: 1.4.8 dev: true - /solid-js/1.4.5: - resolution: {integrity: sha512-32NGpuabEJDTeQ7fjaTR2TLC7R/X5hbqhYdEQ1e+GcIK8r8+/V0Nv17eZQii5Z/97/mtdt8yi63chzg73qnz/A==} - dev: false - /solid-js/1.4.8: resolution: {integrity: sha512-XErZdnnYYXF7OwGSUAPcua2y5/ELB/c53zFCpWiEGqxTNoH1iQghzI8EsHJXk06sNn+Z/TGhb8bPDNNGSgimag==} From 4b4088ec00620eef31c86615ae83c573f7a4a835 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 18:38:31 +0200 Subject: [PATCH 08/13] remove javascript comments from babel tests --- packages/transform/test/wrapStores.test.ts | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/transform/test/wrapStores.test.ts b/packages/transform/test/wrapStores.test.ts index 7ea54bb4..6f7619ae 100644 --- a/packages/transform/test/wrapStores.test.ts +++ b/packages/transform/test/wrapStores.test.ts @@ -18,9 +18,9 @@ function assertTransform(src: string, expectedOutput: string) { describe("createStore", () => { test("named import", () => { - const src = /*javascript*/ `import { createStore } from "solid-js/store";` + const src = `import { createStore } from "solid-js/store";` - const expectedOutput = /*javascript*/ `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; + const expectedOutput = `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; const createStore = (obj, options) => { let wrappedObj = obj; @@ -36,9 +36,9 @@ const createStore = (obj, options) => { }) test("renamed import", () => { - const src = /*javascript*/ `import { createStore as createSolidStore } from "solid-js/store";` + const src = `import { createStore as createSolidStore } from "solid-js/store";` - const expectedOutput = /*javascript*/ `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; + const expectedOutput = `import { createStore as ${storeOverwriteName}0 } from "solid-js/store"; const createSolidStore = (obj, options) => { let wrappedObj = obj; @@ -56,9 +56,9 @@ const createSolidStore = (obj, options) => { describe("createMutable", () => { test("named import", () => { - const src = /*javascript*/ `import { createMutable } from "solid-js/store";` + const src = `import { createMutable } from "solid-js/store";` - const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; + const expectedOutput = `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; const createMutable = (obj, options) => { let wrappedObj = obj; @@ -74,9 +74,9 @@ const createMutable = (obj, options) => { }) test("renamed import", () => { - const src = /*javascript*/ `import { createMutable as createSolidStore } from "solid-js/store";` + const src = `import { createMutable as createSolidStore } from "solid-js/store";` - const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; + const expectedOutput = `import { createMutable as ${storeOverwriteName}0 } from "solid-js/store"; const createSolidStore = (obj, options) => { let wrappedObj = obj; @@ -93,9 +93,9 @@ const createSolidStore = (obj, options) => { }) test("namespace import", () => { - const src = /*javascript*/ `import * as Store from "solid-js/store";` + const src = `import * as Store from "solid-js/store";` - const expectedOutput = /*javascript*/ `import * as ${storeOverwriteNamespace} from "solid-js/store"; + const expectedOutput = `import * as ${storeOverwriteNamespace} from "solid-js/store"; const ${storeOverwriteName}0 = ${storeOverwriteNamespace}.createStore; const ${storeOverwriteName}1 = ${storeOverwriteNamespace}.createMutable; const Store = { ...${storeOverwriteNamespace} @@ -125,9 +125,9 @@ Store.createMutable = (obj, options) => { }) test("both", () => { - const src = /*javascript*/ `import { createMutable, createStore } from "solid-js/store";` + const src = `import { createMutable, createStore } from "solid-js/store";` - const expectedOutput = /*javascript*/ `import { createMutable as ${storeOverwriteName}0, createStore as ${storeOverwriteName}1 } from "solid-js/store"; + const expectedOutput = `import { createMutable as ${storeOverwriteName}0, createStore as ${storeOverwriteName}1 } from "solid-js/store"; const createMutable = (obj, options) => { let wrappedObj = obj; From c0b8b4f6a05f0ad76e1af70bbd7a58b8e65cee6f Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 19:19:15 +0200 Subject: [PATCH 09/13] avoid vite plugin import error --- packages/main/README.md | 5 ++++ packages/main/src/vite.ts | 2 ++ packages/transform/package.json | 4 ++- packages/transform/tsup.config.ts | 2 +- playgrounds/sandbox/vite.config.ts | 42 ++++++++++++++++-------------- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/packages/main/README.md b/packages/main/README.md index 5825294f..2f25837d 100644 --- a/packages/main/README.md +++ b/packages/main/README.md @@ -35,6 +35,10 @@ The `solid-devtools` package comes with the [Locator](https://github.com/thetarn `solid-devtools` reexports the [babel plugin](https://github.com/thetarnav/solid-devtools/tree/main/packages/transform#readme) as a vite plugin. +```**Note** +In some cases import from `solid-devtools/vite` causes errors in loading vite config. I haven't figured out the cause yet. But to avoid the error, you can import from `@solid-devtools/transform` instead. +``` + To enable it you need to add it to plugins array in your `.vite.config.js` file: ```ts @@ -43,6 +47,7 @@ To enable it you need to add it to plugins array in your `.vite.config.js` file: import { defineConfig } from "vite" import solidPlugin from "vite-plugin-solid" import devtoolsPlugin from "solid-devtools/vite" +// or: import devtoolsPlugin from "@solid-devtools/transform" export default defineConfig({ plugins: [devtoolsPlugin(), solidPlugin()], diff --git a/packages/main/src/vite.ts b/packages/main/src/vite.ts index bde95f47..ecd0f484 100644 --- a/packages/main/src/vite.ts +++ b/packages/main/src/vite.ts @@ -1 +1,3 @@ +import devtoolsPlugin from "@solid-devtools/transform" +export default devtoolsPlugin export * from "@solid-devtools/transform" diff --git a/packages/transform/package.json b/packages/transform/package.json index dc7a80be..a70b5193 100644 --- a/packages/transform/package.json +++ b/packages/transform/package.json @@ -22,7 +22,9 @@ "jsx" ], "private": false, - "main": "dist/index.js", + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", "types": "dist/index.d.ts", "files": [ "dist" diff --git a/packages/transform/tsup.config.ts b/packages/transform/tsup.config.ts index 368055fa..e4a448bb 100644 --- a/packages/transform/tsup.config.ts +++ b/packages/transform/tsup.config.ts @@ -1,3 +1,3 @@ import defineConfig from "../../configs/tsup.config" -export default defineConfig("ts", "cjs") +export default defineConfig() diff --git a/playgrounds/sandbox/vite.config.ts b/playgrounds/sandbox/vite.config.ts index 9210abc1..d66fd1d5 100644 --- a/playgrounds/sandbox/vite.config.ts +++ b/playgrounds/sandbox/vite.config.ts @@ -1,29 +1,31 @@ import { defineConfig } from "vite" import solidPlugin from "vite-plugin-solid" -import devtoolsPlugin from "solid-devtools/vite" +import devtoolsPlugin from "@solid-devtools/transform" import path from "path" const pathToPackages = path.resolve(__dirname, "..", "..", "packages") const resolvePackage = (...filepath: string[]) => path.resolve(pathToPackages, ...filepath) export default defineConfig({ - plugins: [devtoolsPlugin(), solidPlugin()], - resolve: { - alias: { - // used inside packages/debugger - "@shared": resolvePackage("shared"), - "solid-devtools": resolvePackage("main", "src"), - "@solid-devtools/debugger": resolvePackage("debugger", "src"), - "@solid-devtools/logger": resolvePackage("logger", "src"), - "@solid-devtools/ui": resolvePackage("ui", "src"), - "@solid-devtools/locator": resolvePackage("locator", "src"), - "@solid-devtools/extension-adapter": resolvePackage("extension-adapter", "src"), - }, - }, - build: { - target: "esnext", - }, - optimizeDeps: { - exclude: ["solid-devtools/vite"], - }, + plugins: [ + devtoolsPlugin({ + wrapStores: true, + }), + solidPlugin(), + ], + resolve: { + alias: { + // used inside packages/debugger + "@shared": resolvePackage("shared"), + "solid-devtools": resolvePackage("main", "src"), + "@solid-devtools/debugger": resolvePackage("debugger", "src"), + "@solid-devtools/logger": resolvePackage("logger", "src"), + "@solid-devtools/ui": resolvePackage("ui", "src"), + "@solid-devtools/locator": resolvePackage("locator", "src"), + "@solid-devtools/extension-adapter": resolvePackage("extension-adapter", "src"), + }, + }, + build: { + target: "esnext", + }, }) From 5baeb50c454da08178184b55b80d76b8d1ee6267 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 20:00:51 +0200 Subject: [PATCH 10/13] basic observe store api --- packages/debugger/env.d.ts | 9 ++ packages/debugger/package.json | 146 +++++++++++------------ packages/debugger/src/index.ts | 45 ++++---- packages/debugger/src/update.ts | 166 +++++++++++++++------------ packages/debugger/tsconfig.json | 4 +- packages/shared/variables.ts | 1 + packages/transform/src/wrapStores.ts | 3 +- playgrounds/sandbox/package.json | 47 ++++---- playgrounds/sandbox/src/Todos.tsx | 161 +++++++++++++------------- playgrounds/sandbox/src/index.tsx | 14 +-- playgrounds/sandbox/vite.config.ts | 1 + pnpm-lock.yaml | 7 +- 12 files changed, 318 insertions(+), 286 deletions(-) create mode 100644 packages/debugger/env.d.ts diff --git a/packages/debugger/env.d.ts b/packages/debugger/env.d.ts new file mode 100644 index 00000000..96b8b38b --- /dev/null +++ b/packages/debugger/env.d.ts @@ -0,0 +1,9 @@ +import type { WINDOW_WRAP_STORE_PROPERTY } from "@shared/variables" + +export {} + +declare global { + interface Window { + [WINDOW_WRAP_STORE_PROPERTY]?: (init: T) => T + } +} diff --git a/packages/debugger/package.json b/packages/debugger/package.json index 18368f8e..070aa310 100644 --- a/packages/debugger/package.json +++ b/packages/debugger/package.json @@ -1,73 +1,77 @@ { - "name": "@solid-devtools/debugger", - "version": "0.2.3", - "description": "Debugger of the Solid's reactivity graph — a cornerstone of all solid-devtools.", - "license": "MIT", - "author": "Damian Tarnawski ", - "contributors": [], - "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/debugger#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/thetarnav/solid-devtools.git" - }, - "bugs": { - "url": "https://github.com/thetarnav/solid-devtools/issues" - }, - "keywords": [ - "solid", - "devtools", - "debugger", - "reactivity" - ], - "private": false, - "sideEffects": false, - "publishConfig": { - "access": "public" - }, - "files": [ - "dist" - ], - "type": "module", - "main": "./dist/server.cjs", - "module": "./dist/server.js", - "types": "./dist/index.d.ts", - "exports": { - "browser": { - "development": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "scripts": { - "dev": "tsup --watch", - "build": "tsup", - "test": "jest --config jest.config.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@testing-library/jest-dom": "^5.16.4", - "jest": "^28.1.3", - "jest-environment-jsdom": "^28.1.3", - "solid-js": "^1.4.8", - "ts-node": "^10.9.1", - "tsup": "^6.2.1", - "typescript": "^4.7.4" - }, - "dependencies": { - "@solid-primitives/event-bus": "^0.1.0", - "@solid-primitives/immutable": "^0.1.0", - "@solid-primitives/memo": "^0.3.0", - "@solid-primitives/refs": "^0.3.0", - "@solid-primitives/scheduled": "^1.0.0", - "@solid-primitives/utils": "^3.0.1" - }, - "peerDependencies": { - "solid-js": "^1.4.5" - }, - "packageManager": "pnpm@7.7.0" + "name": "@solid-devtools/debugger", + "version": "0.2.3", + "description": "Debugger of the Solid's reactivity graph — a cornerstone of all solid-devtools.", + "license": "MIT", + "author": "Damian Tarnawski ", + "contributors": [], + "homepage": "https://github.com/thetarnav/solid-devtools/tree/main/packages/debugger#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thetarnav/solid-devtools.git" + }, + "bugs": { + "url": "https://github.com/thetarnav/solid-devtools/issues" + }, + "keywords": [ + "solid", + "devtools", + "debugger", + "reactivity" + ], + "private": false, + "sideEffects": false, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/server.cjs", + "module": "./dist/server.js", + "types": "./dist/index.d.ts", + "exports": { + "browser": { + "development": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "test": "jest --config jest.config.ts", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@testing-library/jest-dom": "^5.16.4", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "solid-js": "^1.4.8", + "ts-node": "^10.9.1", + "tsup": "^6.2.1", + "typescript": "^4.7.4" + }, + "dependencies": { + "@solid-primitives/event-bus": "^0.1.0", + "@solid-primitives/immutable": "^0.1.0", + "@solid-primitives/memo": "^0.3.0", + "@solid-primitives/refs": "^0.3.0", + "@solid-primitives/scheduled": "^1.0.0", + "@solid-primitives/utils": "^3.0.1", + "object-observer": "^5.0.4" + }, + "peerDependencies": { + "solid-js": "^1.4.5" + }, + "optionalDependencies": { + "@solid-devtools/transform": "^0.3.1" + }, + "packageManager": "pnpm@7.7.0" } diff --git a/packages/debugger/src/index.ts b/packages/debugger/src/index.ts index 3fb884b4..fe049e6d 100644 --- a/packages/debugger/src/index.ts +++ b/packages/debugger/src/index.ts @@ -9,32 +9,33 @@ export type { TargetIDE, TargetURLFunction } from "@solid-devtools/locator" export { attachDebugger } from "./primitives" export { - makeSolidUpdateListener, - makeCreateRootListener, - observeComputationUpdate, - observeValueUpdate, - interceptComputationRerun, + makeSolidUpdateListener, + makeCreateRootListener, + makeStoreObserver, + observeComputationUpdate, + observeValueUpdate, + interceptComputationRerun, } from "./update" -export type { AfterCrateRoot } from "./update" +export type { AfterCrateRoot, ObjectObserver } from "./update" export { - getOwnerType, - getNodeType, - getNodeName, - lookupOwner, - isSolidComputation, - isSolidMemo, - isSolidOwner, - isSolidRoot, - onOwnerCleanup, - onParentCleanup, - getFunctionSources, - getSafeValue, - createUnownedRoot, - createInternalRoot, + getOwnerType, + getNodeType, + getNodeName, + lookupOwner, + isSolidComputation, + isSolidMemo, + isSolidOwner, + isSolidRoot, + onOwnerCleanup, + onParentCleanup, + getFunctionSources, + getSafeValue, + createUnownedRoot, + createInternalRoot, } from "./utils" export const Debugger: ParentComponent = props => { - attachDebugger() - return props.children + attachDebugger() + return props.children } diff --git a/packages/debugger/src/update.ts b/packages/debugger/src/update.ts index abff5a56..d3112e84 100644 --- a/packages/debugger/src/update.ts +++ b/packages/debugger/src/update.ts @@ -1,25 +1,23 @@ -import { onCleanup } from "solid-js" -import { - getOwner, - SignalState, - SolidComputation, - SolidRoot, - ValueUpdateListener, -} from "@shared/graph" +import { noop, onRootCleanup } from "@solid-primitives/utils" +import { Observable, Observer as ObjectObserver } from "object-observer" +import { SignalState, SolidComputation, SolidRoot, ValueUpdateListener } from "@shared/graph" import { Owner } from "@shared/solid" +import { WINDOW_WRAP_STORE_PROPERTY } from "@shared/variables" import { skipInternalRoot } from "./utils" +// // AFTER UPDATE +// const GraphUpdateListeners = new Set() // Patch window._$afterUpdate { - const runListeners = () => GraphUpdateListeners.forEach(f => f()) - if (typeof window._$afterUpdate === "function") { - GraphUpdateListeners.add(window._$afterUpdate) - } - window._$afterUpdate = runListeners + const runListeners = () => GraphUpdateListeners.forEach(f => f()) + if (typeof window._$afterUpdate === "function") { + GraphUpdateListeners.add(window._$afterUpdate) + } + window._$afterUpdate = runListeners } /** @@ -29,13 +27,15 @@ const GraphUpdateListeners = new Set() * This will listen to all updates of the reactive graph — including ones outside of the component, and debugger internal computations. */ export function makeSolidUpdateListener(onUpdate: VoidFunction): VoidFunction { - GraphUpdateListeners.add(onUpdate) - const unsub = () => GraphUpdateListeners.delete(onUpdate) - getOwner() && onCleanup(unsub) - return unsub + GraphUpdateListeners.add(onUpdate) + return onRootCleanup(() => { + GraphUpdateListeners.delete(onUpdate) + }) } +// // AFTER CREATE ROOT +// export type AfterCrateRoot = (root: SolidRoot) => void @@ -43,14 +43,14 @@ const CreateRootListeners = new Set() // Patch window._$afterCreateRoot { - const runListeners: AfterCrateRoot = root => { - if (skipInternalRoot()) return - CreateRootListeners.forEach(f => f(root)) - } - if (typeof window._$afterCreateRoot === "function") { - CreateRootListeners.add(window._$afterCreateRoot) - } - window._$afterCreateRoot = runListeners as (root: Owner) => void + const runListeners: AfterCrateRoot = root => { + if (skipInternalRoot()) return + CreateRootListeners.forEach(f => f(root)) + } + if (typeof window._$afterCreateRoot === "function") { + CreateRootListeners.add(window._$afterCreateRoot) + } + window._$afterCreateRoot = runListeners as (root: Owner) => void } /** @@ -58,24 +58,46 @@ const CreateRootListeners = new Set() * The listener is automatically cleaned-up on root dispose. */ export function makeCreateRootListener(onUpdate: AfterCrateRoot): VoidFunction { - CreateRootListeners.add(onUpdate) - const unsub = () => CreateRootListeners.delete(onUpdate) - getOwner() && onCleanup(unsub) - return unsub + CreateRootListeners.add(onUpdate) + return onRootCleanup(() => CreateRootListeners.delete(onUpdate)) } +// +// WRAP STORES +// + +export type { ObjectObserver } + +// window[WINDOW_WRAP_STORE_PROPERTY] is internal — no need to worry about other users +window[WINDOW_WRAP_STORE_PROPERTY] = init => { + return Observable.from(init) +} + +export function makeStoreObserver(state: object, onUpdate: ObjectObserver): VoidFunction { + if (!Observable.isObservable(state)) { + console.warn(`Object ${state} is not wrapped`) + return noop + } + Observable.observe(state, onUpdate) + return onRootCleanup(() => Observable.unobserve(state, onUpdate)) +} + +// +// OBSERVE NODES +// + /** * Wraps the fn prop of owner object to trigger handler whenever the computation is executed. */ export function observeComputationUpdate(owner: SolidComputation, onRun: VoidFunction): void { - // owner already patched - if (owner.onComputationUpdate) return void (owner.onComputationUpdate = onRun) - // patch owner - owner.onComputationUpdate = onRun - interceptComputationRerun(owner, fn => { - owner.onComputationUpdate!() - fn() - }) + // owner already patched + if (owner.onComputationUpdate) return void (owner.onComputationUpdate = onRun) + // patch owner + owner.onComputationUpdate = onRun + interceptComputationRerun(owner, fn => { + owner.onComputationUpdate!() + fn() + }) } /** @@ -95,47 +117,47 @@ export function observeComputationUpdate(owner: SolidComputation, onRun: VoidFun * ``` */ export function interceptComputationRerun( - owner: SolidComputation, - onRun: (execute: () => T, prev: T) => void, + owner: SolidComputation, + onRun: (execute: () => T, prev: T) => void, ): void { - const _fn = owner.fn - let v!: unknown - let prev!: unknown - const fn = () => (v = _fn(prev)) - owner.fn = !!owner.fn.length - ? p => { - onRun(fn, (prev = p)) - return v - } - : () => { - onRun(fn, undefined) - return v - } + const _fn = owner.fn + let v!: unknown + let prev!: unknown + const fn = () => (v = _fn(prev)) + owner.fn = !!owner.fn.length + ? p => { + onRun(fn, (prev = p)) + return v + } + : () => { + onRun(fn, undefined) + return v + } } /** * Patches the owner/signal value, firing the callback on each update immediately as it happened. */ export function observeValueUpdate( - node: SignalState, - onUpdate: ValueUpdateListener, - symbol: symbol, + node: SignalState, + onUpdate: ValueUpdateListener, + symbol: symbol, ): VoidFunction { - const remove = () => delete node.onValueUpdate![symbol] - // node already patched - if (node.onValueUpdate) { - node.onValueUpdate[symbol] = onUpdate - return remove - } - // patch node - const map = (node.onValueUpdate = { [symbol]: onUpdate }) - let value = node.value - Object.defineProperty(node, "value", { - get: () => value, - set: newValue => { - for (let sym of Object.getOwnPropertySymbols(map)) map[sym](newValue, value) - value = newValue - }, - }) - return remove + const remove = () => delete node.onValueUpdate![symbol] + // node already patched + if (node.onValueUpdate) { + node.onValueUpdate[symbol] = onUpdate + return remove + } + // patch node + const map = (node.onValueUpdate = { [symbol]: onUpdate }) + let value = node.value + Object.defineProperty(node, "value", { + get: () => value, + set: newValue => { + for (let sym of Object.getOwnPropertySymbols(map)) map[sym](newValue, value) + value = newValue + }, + }) + return remove } diff --git a/packages/debugger/tsconfig.json b/packages/debugger/tsconfig.json index 6f1cc4ba..0ed3198f 100644 --- a/packages/debugger/tsconfig.json +++ b/packages/debugger/tsconfig.json @@ -1,4 +1,4 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*", "test/**/*", "../../env.d.ts"] + "extends": "../../tsconfig.json", + "include": ["src/**/*", "test/**/*", "../../env.d.ts", "env.d.ts"] } diff --git a/packages/shared/variables.ts b/packages/shared/variables.ts index 06575f21..55df5e89 100644 --- a/packages/shared/variables.ts +++ b/packages/shared/variables.ts @@ -1,4 +1,5 @@ export const WINDOW_PROJECTPATH_PROPERTY = "$sdt_projectPath" +export const WINDOW_WRAP_STORE_PROPERTY = "$sdt_wrapStore" export const LOCATION_ATTRIBUTE_NAME = "data-source-loc" export const UNNAMED = "(unnamed)" export const INTERNAL = Symbol("internal") diff --git a/packages/transform/src/wrapStores.ts b/packages/transform/src/wrapStores.ts index 7a7b6788..b38fb153 100644 --- a/packages/transform/src/wrapStores.ts +++ b/packages/transform/src/wrapStores.ts @@ -1,5 +1,6 @@ import { PluginObj } from "@babel/core" import * as t from "@babel/types" +import { WINDOW_WRAP_STORE_PROPERTY } from "@shared/variables" import { storeOverwriteName, storeOverwriteNamespace, windowId } from "./utils" const createStoreId = t.identifier("createStore") @@ -21,7 +22,7 @@ function getStoreWrapperArrowFn(overwrite: t.Identifier): t.ArrowFunctionExpress const objId = t.identifier("obj") const optionsId = t.identifier("options") const wrappedObjId = t.identifier("wrappedObj") - const wrapStoreId = t.identifier("$sdt_wrapStore") + const wrapStoreId = t.identifier(WINDOW_WRAP_STORE_PROPERTY) const wrapStoreMember = t.memberExpression(windowId, wrapStoreId) return t.arrowFunctionExpression( diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index bcb8ef2c..fd12b79f 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -1,26 +1,25 @@ { - "name": "solid-devtools-sandbox-playground", - "version": "0.0.1", - "private": true, - "description": "", - "scripts": { - "start": "vite", - "dev": "vite", - "build": "vite build", - "serve": "vite preview" - }, - "license": "MIT", - "devDependencies": { - "@solid-devtools/transform": "^0.3.1", - "typescript": "^4.7.4", - "vite": "^3.0.4", - "vite-plugin-solid": "^2.3.0" - }, - "dependencies": { - "@solid-devtools/logger": "^0.2.1", - "@solid-primitives/timer": "^1.3.1", - "object-observer": "^5.0.4", - "solid-devtools": "^0.8.1", - "solid-js": "^1.4.8" - } + "name": "solid-devtools-sandbox-playground", + "version": "0.0.1", + "private": true, + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "devDependencies": { + "@solid-devtools/transform": "^0.3.1", + "typescript": "^4.7.4", + "vite": "^3.0.4", + "vite-plugin-solid": "^2.3.0" + }, + "dependencies": { + "@solid-devtools/logger": "^0.2.1", + "@solid-primitives/timer": "^1.3.1", + "solid-devtools": "^0.8.1", + "solid-js": "^1.4.8" + } } diff --git a/playgrounds/sandbox/src/Todos.tsx b/playgrounds/sandbox/src/Todos.tsx index 1e60ef2c..e16f40a9 100644 --- a/playgrounds/sandbox/src/Todos.tsx +++ b/playgrounds/sandbox/src/Todos.tsx @@ -1,101 +1,104 @@ -import { debugProps, debugStore } from "@solid-devtools/logger" import { createEffect, createSignal, batch, For, Component } from "solid-js" -import { reconcile } from "solid-js/store" -import * as Store from "solid-js/store" +import { $RAW, reconcile, unwrap } from "solid-js/store" +import { createStore, Store, SetStoreFunction } from "solid-js/store" +import { debugProps } from "@solid-devtools/logger" +import { makeStoreObserver } from "@solid-devtools/debugger" export function createLocalStore( - name: string, - init: T, -): [Store.Store, Store.SetStoreFunction] { - const localState = localStorage.getItem(name) - const [state, setState] = Store.createStore(localState ? JSON.parse(localState) : init) - createEffect(() => localStorage.setItem(name, JSON.stringify(state))) - return [state, setState] + name: string, + init: T, +): [Store, SetStoreFunction] { + const localState = localStorage.getItem(name) + const [state, setState] = createStore(localState ? JSON.parse(localState) : init) + createEffect(() => localStorage.setItem(name, JSON.stringify(state))) + return [state, setState] } export function removeIndex(array: readonly T[], index: number): T[] { - return [...array.slice(0, index), ...array.slice(index + 1)] + return [...array.slice(0, index), ...array.slice(index + 1)] } type TodoItem = { title: string; done: boolean } const Todo: Component<{ - done: boolean - title: string - onCheck: (value: boolean) => void - onUpdate: (value: string) => void - onRemove: VoidFunction + done: boolean + title: string + onCheck: (value: boolean) => void + onUpdate: (value: string) => void + onRemove: VoidFunction }> = props => { - // debugProps(props) + // debugProps(props) - return ( -
- props.onCheck(e.currentTarget.checked)} - /> - props.onUpdate(e.currentTarget.value)} - /> - -
- ) + return ( +
+ props.onCheck(e.currentTarget.checked)} + /> + props.onUpdate(e.currentTarget.value)} + /> + +
+ ) } const Todos: Component = () => { - const [newTitle, setTitle] = createSignal("") - const [todos, setTodos] = createLocalStore("todos", []) + const [newTitle, setTitle] = createSignal("") + const [todos, setTodos] = createLocalStore("todos", []) - // debugStore(todos) + makeStoreObserver(todos, console.log) - const addTodo = (e: SubmitEvent) => { - e.preventDefault() - batch(() => { - setTodos(todos.length, { - title: newTitle(), - done: false, - }) - setTitle("") - }) - } + // debugStore(todos) - // setTimeout(() => { - // setTodos( - // 0, - // reconcile({ - // title: "Learn Solid-JS", - // done: false, - // [Math.random() + ""]: "hello", - // }), - // ) - // }, 1000) + const addTodo = (e: SubmitEvent) => { + e.preventDefault() + batch(() => { + setTodos(todos.length, { + title: newTitle(), + done: false, + }) + setTitle("") + }) + } - return ( - <> -

Simple Todos Example

-
- setTitle(e.currentTarget.value)} - /> - -
- - {(todo, i) => ( - setTodos(i(), "done", v)} - onUpdate={v => setTodos(i(), "title", v)} - onRemove={() => setTodos(t => removeIndex(t, i()))} - /> - )} - - - ) + // setTimeout(() => { + // setTodos( + // 0, + // reconcile({ + // title: "Learn Solid-JS", + // done: false, + // [Math.random() + ""]: "hello", + // }), + // ) + // }, 1000) + + return ( + <> +

Simple Todos Example

+
+ setTitle(e.currentTarget.value)} + /> + +
+ + {(todo, i) => ( + setTodos(i(), "done", v)} + onUpdate={v => setTodos(i(), "title", v)} + onRemove={() => setTodos(t => removeIndex(t, i()))} + /> + )} + + + ) } export default Todos diff --git a/playgrounds/sandbox/src/index.tsx b/playgrounds/sandbox/src/index.tsx index 3a9cc5ba..0528e875 100644 --- a/playgrounds/sandbox/src/index.tsx +++ b/playgrounds/sandbox/src/index.tsx @@ -1,23 +1,11 @@ /* @refresh reload */ import { render } from "solid-js/web" import { useLocatorPlugin } from "solid-devtools" -import { Observable } from "object-observer" - -window["$sdt_wrapStore"] = obj => { - console.log("storeCreated") - const observed = Observable.from(obj) - Observable.observe(observed, changes => { - changes.forEach(change => { - console.log(change) - }) - }) - return observed -} import App from "./App" useLocatorPlugin({ - targetIDE: "vscode", + targetIDE: "vscode", }) export const disposeApp = render(() => , document.getElementById("root")!) diff --git a/playgrounds/sandbox/vite.config.ts b/playgrounds/sandbox/vite.config.ts index d66fd1d5..7a951857 100644 --- a/playgrounds/sandbox/vite.config.ts +++ b/playgrounds/sandbox/vite.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ plugins: [ devtoolsPlugin({ wrapStores: true, + jsxLocation: true, }), solidPlugin(), ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3defd41..fc113762 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,7 @@ importers: packages/debugger: specifiers: + '@solid-devtools/transform': ^0.3.1 '@solid-primitives/event-bus': ^0.1.0 '@solid-primitives/immutable': ^0.1.0 '@solid-primitives/memo': ^0.3.0 @@ -49,6 +50,7 @@ importers: '@testing-library/jest-dom': ^5.16.4 jest: ^28.1.3 jest-environment-jsdom: ^28.1.3 + object-observer: ^5.0.4 solid-js: ^1.4.8 ts-node: ^10.9.1 tsup: ^6.2.1 @@ -60,6 +62,9 @@ importers: '@solid-primitives/refs': 0.3.0_solid-js@1.4.8 '@solid-primitives/scheduled': 1.0.0_solid-js@1.4.8 '@solid-primitives/utils': 3.0.1_solid-js@1.4.8 + object-observer: 5.0.4 + optionalDependencies: + '@solid-devtools/transform': link:../transform devDependencies: '@testing-library/jest-dom': 5.16.4 jest: 28.1.3_g3ob4elhesv7uyo2jbxkqs7hje @@ -322,7 +327,6 @@ importers: '@solid-devtools/logger': ^0.2.1 '@solid-devtools/transform': ^0.3.1 '@solid-primitives/timer': ^1.3.1 - object-observer: ^5.0.4 solid-devtools: ^0.8.1 solid-js: ^1.4.8 typescript: ^4.7.4 @@ -331,7 +335,6 @@ importers: dependencies: '@solid-devtools/logger': link:../../packages/logger '@solid-primitives/timer': 1.3.1_solid-js@1.4.8 - object-observer: 5.0.4 solid-devtools: link:../../packages/main solid-js: 1.4.8 devDependencies: From 000bdcdf296609fa5415eb6eb31b5b660bc25ca5 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 20:01:32 +0200 Subject: [PATCH 11/13] add server export for makeStoreObserver --- packages/debugger/src/server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/debugger/src/server.ts b/packages/debugger/src/server.ts index 55697ead..08194cd9 100644 --- a/packages/debugger/src/server.ts +++ b/packages/debugger/src/server.ts @@ -15,6 +15,7 @@ export const registerDebuggerPlugin: typeof API.registerDebuggerPlugin = noop // update export const makeSolidUpdateListener: typeof API.makeSolidUpdateListener = () => noop export const makeCreateRootListener: typeof API.makeCreateRootListener = () => noop +export const makeStoreObserver: typeof API.makeStoreObserver = () => noop export const observeComputationUpdate: typeof API.observeComputationUpdate = noop export const observeValueUpdate: typeof API.observeValueUpdate = () => noop export const interceptComputationRerun: typeof API.interceptComputationRerun = noop From 3c140cccd81951469b3450947000b281edddd1e0 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 20:09:14 +0200 Subject: [PATCH 12/13] correct for typecheck run & add changeset --- .changeset/swift-panthers-pump.md | 5 +++++ packages/debugger/env.d.ts | 9 --------- packages/debugger/src/update.ts | 6 ++++++ packages/debugger/tsconfig.json | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 .changeset/swift-panthers-pump.md delete mode 100644 packages/debugger/env.d.ts diff --git a/.changeset/swift-panthers-pump.md b/.changeset/swift-panthers-pump.md new file mode 100644 index 00000000..5456bc0a --- /dev/null +++ b/.changeset/swift-panthers-pump.md @@ -0,0 +1,5 @@ +--- +"@solid-devtools/debugger": minor +--- + +Add basic API for observing store updates — `makeStoreObserver`. diff --git a/packages/debugger/env.d.ts b/packages/debugger/env.d.ts deleted file mode 100644 index 96b8b38b..00000000 --- a/packages/debugger/env.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { WINDOW_WRAP_STORE_PROPERTY } from "@shared/variables" - -export {} - -declare global { - interface Window { - [WINDOW_WRAP_STORE_PROPERTY]?: (init: T) => T - } -} diff --git a/packages/debugger/src/update.ts b/packages/debugger/src/update.ts index d3112e84..db0ee179 100644 --- a/packages/debugger/src/update.ts +++ b/packages/debugger/src/update.ts @@ -68,6 +68,12 @@ export function makeCreateRootListener(onUpdate: AfterCrateRoot): VoidFunction { export type { ObjectObserver } +declare global { + interface Window { + [WINDOW_WRAP_STORE_PROPERTY]?: (init: T) => T + } +} + // window[WINDOW_WRAP_STORE_PROPERTY] is internal — no need to worry about other users window[WINDOW_WRAP_STORE_PROPERTY] = init => { return Observable.from(init) diff --git a/packages/debugger/tsconfig.json b/packages/debugger/tsconfig.json index 0ed3198f..4f13fc88 100644 --- a/packages/debugger/tsconfig.json +++ b/packages/debugger/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*", "test/**/*", "../../env.d.ts", "env.d.ts"] + "include": ["src/**/*", "test/**/*", "../../env.d.ts"] } From bdd1f80ba693a27540b7e0220df286a2e2485f91 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 3 Aug 2022 20:16:14 +0200 Subject: [PATCH 13/13] add jest module mock for "object-observer" --- configs/jest.setup.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/configs/jest.setup.ts b/configs/jest.setup.ts index e92fb18f..00fef1b9 100644 --- a/configs/jest.setup.ts +++ b/configs/jest.setup.ts @@ -1,7 +1,15 @@ jest.mock("@otonashixav/solid-flip", () => ({ - TransitionGroup: (p: any) => p.children, - animateExit: () => {}, - animateEnter: () => {}, + TransitionGroup: (p: any) => p.children, + animateExit: () => {}, + animateEnter: () => {}, +})) + +jest.mock("object-observer", () => ({ + Observable: { + from: (obj: any) => obj, + observe: () => void 0, + unobserve: () => void 0, + }, })) export {}