diff --git a/.changeset/witty-foxes-lick.md b/.changeset/witty-foxes-lick.md new file mode 100644 index 00000000..598f4faf --- /dev/null +++ b/.changeset/witty-foxes-lick.md @@ -0,0 +1,8 @@ +--- +"@solid-devtools/extension": patch +--- + +Rewrite and simplify the background script. +The devtools should be more reliable when switching tabs, closing, reloading etc. +The icon of the extension on the actions bar should light up when on site with solid detected. + diff --git a/packages/extension/src/background.ts b/packages/extension/src/background.ts index 68add328..43ec1745 100644 --- a/packages/extension/src/background.ts +++ b/packages/extension/src/background.ts @@ -7,279 +7,287 @@ It has to coordinate the communication between the different scripts based on th */ import {error, log} from '@solid-devtools/shared/utils' -import * as debug from '@solid-devtools/debugger/types' import * as bridge from './bridge.ts' import * as icons from './icons.ts' + log(bridge.Place_Name.Background+' loaded.') -type PortMessanger = bridge.PortMessanger -class TabData { +type Tab_Id = number & {__Tab_Id__: true} - id: number = -1 +let active_tab_id: Tab_Id = 0 as Tab_Id - panel_messanger: PortMessanger | undefined - popup_messanger: PortMessanger | undefined - devtools_messanger: PortMessanger | undefined +chrome.tabs.onActivated.addListener((info) => { + active_tab_id = info.tabId as Tab_Id +}) - content: undefined | { - messanger: PortMessanger - detected_state: bridge.DetectionState | undefined - versions: bridge.Versions | undefined +function assert(condition: any, message?: string, cause?: any): asserts condition { + if (!condition) { + throw Error(message ?? 'Assertion failed', {cause}) } } -const ACTIVE_TAB_QUERY = {active: true, currentWindow: true} as const -const queryActiveTabId = async (): Promise => { - try { - let tabs = await chrome.tabs.query(ACTIVE_TAB_QUERY) - if (tabs.length === 0) return new Error('No active tab') +function get_assert_tab_id(port: Port, place: bridge.Place_Name): Tab_Id { + let tab_id = port.sender?.tab?.id + assert(tab_id, `${place} has no port sender tab id.`, port) + return tab_id as Tab_Id +} - let tab = tabs[0]! - if (!tab.id) return new Error('Active tab has no id') +type Port = chrome.runtime.Port - return tab.id - } catch (e) { - return e instanceof Error ? e : new Error('Unknown error') - } +function post_message( + port: Port, name: K, details: bridge.Channels[K], +) { + port.postMessage({name, details}) +} +function post_message_obj(port: Port, e: bridge.Message) { + port.postMessage(e) +} + +type Script_Popup = { + port: Port +} +type Script_Panel = { + tab_id: Tab_Id + port: Port +} +type Script_Devtools = { + tab_id: Tab_Id + port: Port +} +type Script_Content = { + tab_id: Tab_Id + port: Port + detection: bridge.DetectionState | null + versions: bridge.Versions | null } -const tab_data_map = new Map() +let popup: Script_Popup | undefined -const getActiveTabData = async (): Promise => { +const script_panel_map = new Map() +const script_devtools_map = new Map() +const script_content_map = new Map() - let active_tab_id = await queryActiveTabId() - if (active_tab_id instanceof Error) return active_tab_id +chrome.runtime.onConnect.addListener(port => { - let data = tab_data_map.get(active_tab_id) - if (!data) return new Error(`No data for active tab "${active_tab_id}"`) + on_connected(port) - return data -} + port.onMessage.addListener(_e => { + let e = bridge.to_message(_e) + if (e) on_message(port, e) + }) -// for reconnecting after page reload -let last_disconnected_tab: TabData | null = null + port.onDisconnect.addListener(() => { + on_disconnected(port) + }) +}) + +function on_connected(port: Port) { -chrome.runtime.onConnect.addListener(async port => { + DEV: {log('Port connected', port)} switch (port.name) { - case bridge.ConnectionName.Content: { - const tab_id = port.sender?.tab?.id - if (typeof tab_id !== 'number') break + case bridge.ConnectionName.Popup: { + popup = {port} - const content_messanger = bridge.createPortMessanger( - bridge.Place_Name.Background, - bridge.Place_Name.Content_Script, - port) + let content = script_content_map.get(active_tab_id) + if (content) { + post_message(popup.port, 'Detected', content.detection) + post_message(popup.port, 'Versions', content.versions) + } - let tab: TabData + break + } + case bridge.ConnectionName.Content: { + let tab_id = get_assert_tab_id(port, bridge.Place_Name.Content) - // Page was reloaded, so we need to reinitialize the tab data - if (tab_id === last_disconnected_tab?.id) { - tab = last_disconnected_tab + let content: Script_Content = { + port: port, + tab_id: tab_id, + detection: null, + versions: null, } - // A fresh page - else { - tab = new TabData + script_content_map.set(tab_id, content) + + let panel = script_panel_map.get(tab_id) + if (panel) { + post_message(content.port, 'DevtoolsOpened', true) + + post_message_obj(content.port, { + name: 'ResetState', + details: undefined, + forwarding: true, + }) } - tab.id = tab_id - tab.content = { - messanger: content_messanger, - versions: undefined, - detected_state: undefined, + break + } + case bridge.ConnectionName.Devtools: { + + let devtools: Script_Devtools = {port, tab_id: active_tab_id} + script_devtools_map.set(active_tab_id, devtools) + + let content = script_content_map.get(active_tab_id) + if (content) { + post_message(port, 'Versions', content.versions) } - last_disconnected_tab = null + break + } + case bridge.ConnectionName.Panel: { - tab_data_map.set(tab_id, tab) + let panel: Script_Panel = {port, tab_id: active_tab_id} + script_panel_map.set(active_tab_id, panel) - panel_and_content_connect(tab) + let content = script_content_map.get(active_tab_id) + if (content) { + post_message(port, 'Versions', content.versions) - // "Versions" from content-script - bridge.once(content_messanger.on, 'Versions', v => { + post_message(content.port, 'DevtoolsOpened', true) - tab.content!.versions = v + post_message_obj(content.port, { + name: 'ResetState', + details: undefined, + forwarding: true, + }) + } - if (tab.devtools_messanger) { - tab.devtools_messanger.post('Versions', v) - } + break + } + } +} - if (tab.panel_messanger) { - panel_handle_versions(tab, tab.panel_messanger, v) - } +function on_disconnected(port: Port) { - if (tab.popup_messanger) { - tab.popup_messanger.post('Versions', v) - } + DEV: {log('Port disconnected', port)} - /* Change the popup icon to indicate that Solid is present on the page */ - chrome.action.setIcon({tabId: tab_id, path: icons.blue}) - }) - - /* "DetectSolid" from content-script (realWorld) */ - content_messanger.on('Detected', state => { - tab.popup_messanger?.post('Detected', state) - tab.content!.detected_state = state - }) - - /* HANDLE FORWARDED MESSAGES FROM CLIENT (content-script) */ - port.onMessage.addListener((e: bridge.ForwardPayload | any) => { - if (bridge.isForwardMessage(e)) { - if (tab.panel_messanger) { - tab.panel_messanger.post(e.name as any, e.details) - } else { - error(`Cannot forward ${bridge.Place_Name.Content_Script} -> ${bridge.Place_Name.Panel} - ${e.name}:`, e.details) - } - } - }) + switch (port.name) { + case bridge.ConnectionName.Popup: { + popup = undefined + break + } + case bridge.ConnectionName.Content: { + let tab_id = get_assert_tab_id(port, bridge.Place_Name.Content) + script_content_map.delete(tab_id) - /* Content Script Disconnected */ - port.onDisconnect.addListener(() => { + if (popup) { + post_message(popup.port, 'Detected', null) + post_message(popup.port, 'Versions', null) + } - if (tab.panel_messanger) { - tab.panel_messanger.post('ResetPanel') - } + let panel = script_panel_map.get(tab_id) + if (panel) { + post_message(panel.port, 'Versions', null) + post_message(panel.port, 'ResetPanel', undefined) + } - tab.content = undefined + let devtools = script_devtools_map.get(tab_id) + if (devtools) { + post_message(devtools.port, 'Versions', null) + } - tab_data_map.delete(tab_id) - last_disconnected_tab = tab - }) + // Change the popup icon back to gray + chrome.action.setIcon({tabId: tab_id, path: icons.gray}) break } - case bridge.ConnectionName.Devtools: { - let tab = await getActiveTabData() - if (tab instanceof Error) { - error(tab) - break + script_devtools_map.delete(active_tab_id) + + let content = script_content_map.get(active_tab_id) + if (content) { + post_message(content.port, 'DevtoolsOpened', false) } - let devtools_messanger = bridge.createPortMessanger( - bridge.Place_Name.Background, - bridge.Place_Name.Devtools_Script, - port) - tab.devtools_messanger = devtools_messanger + break + } + case bridge.ConnectionName.Panel: { + script_panel_map.delete(active_tab_id) - if (tab.content && tab.content.versions) { - devtools_messanger.post('Versions', tab.content.versions) + let content = script_content_map.get(active_tab_id) + if (content) { + post_message(content.port, 'DevtoolsOpened', false) } - /* Devtools Script Disconnected */ - port.onDisconnect.addListener(() => { + break + } + } +} - tab.devtools_messanger = undefined +function on_message(port: Port, e: bridge.Message) { - if (tab.content) { - tab.content.messanger.post('DevtoolsClosed') - } - }) + DEV: {log('Message', e, 'from', port)} + switch (port.name) { + case bridge.ConnectionName.Popup: { break } + case bridge.ConnectionName.Content: { + let tab_id = get_assert_tab_id(port, bridge.Place_Name.Content) - case bridge.ConnectionName.Panel: { - let tab = await getActiveTabData() - if (tab instanceof Error) { - error(tab) - break - } - - let panel_messanger = bridge.createPortMessanger( - bridge.Place_Name.Background, - bridge.Place_Name.Panel, - port) - tab.panel_messanger = panel_messanger + let content = script_content_map.get(tab_id) + assert(content) - panel_and_content_connect(tab) + // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check + switch (e.name) { + case 'Detected': { + content.detection = e.details - // Forward messages from Panel to Content Script (client) - panel_messanger.onForward(e => { - if (tab.content) { - tab.content.messanger.forward(e) - } else { - error(`Cannot forward ${bridge.Place_Name.Panel} -> ${bridge.Place_Name.Content_Script} - ${e.name}:`, e.details) + if (popup) { + post_message_obj(popup.port, e) } - }) - /* Panel Disconnected */ - port.onDisconnect.addListener(() => { + // Change the popup icon to indicate that Solid is present on the page + chrome.action.setIcon({tabId: tab_id, path: icons.blue}) - tab.panel_messanger = undefined + break + } + case 'Versions': { + content.versions = e.details - if (tab.content) { - tab.content.messanger.post('DevtoolsClosed') + if (popup) { + post_message_obj(popup.port, e) } - }) - break - } + let devtools = script_devtools_map.get(tab_id) + if (devtools) { + post_message_obj(devtools.port, e) + } + + let panel = script_panel_map.get(tab_id) + if (panel) { + post_message_obj(panel.port, e) + } - case bridge.ConnectionName.Popup: { - let tab = await getActiveTabData() - if (tab instanceof Error) { - error(tab) break } - let popup_messanger = bridge.createPortMessanger( - bridge.Place_Name.Background, - bridge.Place_Name.Popup, - port) - tab.popup_messanger = popup_messanger - - if (tab.content && tab.content.versions) { - popup_messanger.post('Versions', tab.content.versions) + default: { + // Forward all other messages to panel + let panel = script_panel_map.get(tab_id) + if (panel) { + post_message_obj(panel.port, e) + } } - - if (tab.content && tab.content.detected_state) { - popup_messanger.post('Detected', tab.content.detected_state) } - port.onDisconnect.addListener(() => { - tab.popup_messanger = undefined - }) - break } + case bridge.ConnectionName.Devtools: { + break } -}) - -function panel_handle_versions(tab: TabData, panel_messanger: PortMessanger, versions: bridge.Versions) { - - panel_messanger.post('Versions', versions) + case bridge.ConnectionName.Panel: { - /* tell client that the devtools panel is ready */ - if (tab.content) { - tab.content.messanger.post('DevtoolsOpened') - } else { - error(`Versions available while ${bridge.Place_Name.Content_Script} not connected.`) + // Forward all messages to Content + let content = script_content_map.get(active_tab_id) + if (content) { + post_message_obj(content.port, e) + } else { + error(`Cannot forward ${bridge.Place_Name.Panel} -> ${bridge.Place_Name.Content} - ${e.name}:`, e.details) + } + + break } -} - -/** - To be called whenever direct connection between content-script and panel needs to be recreated - Like when page refreshes or panel gets closed and opened -*/ -function panel_and_content_connect(tab: TabData) { - - if (!tab.content || !tab.panel_messanger) - return - - /* Client is already connected */ - if (tab.content.versions) { - panel_handle_versions(tab, tab.panel_messanger, tab.content.versions) } - - /* Force debugger to send state when panel conects */ - tab.content.messanger.forward({ - name: 'ResetState', - details: undefined, - forwarding: true, - }) } - diff --git a/packages/extension/src/bridge.ts b/packages/extension/src/bridge.ts index cedcd701..8f84110d 100644 --- a/packages/extension/src/bridge.ts +++ b/packages/extension/src/bridge.ts @@ -5,12 +5,13 @@ File for utilities, constants and types related to the communication between the */ import {error, log, log_message} from '@solid-devtools/shared/utils' +import * as debug from '@solid-devtools/debugger/types' export const DEVTOOLS_ID_PREFIX = '[solid-devtools]_' export const enum Place_Name { - Content_Script = 'Content_Script', - Devtools_Script = 'Devtools_Script', + Content = 'Content_Script', + Devtools = 'Devtools_Script', Popup = 'Popup', Panel = 'Panel', Background = 'Background', @@ -19,8 +20,8 @@ export const enum Place_Name { } export const enum ConnectionName { - Content = DEVTOOLS_ID_PREFIX+Place_Name.Content_Script, - Devtools = DEVTOOLS_ID_PREFIX+Place_Name.Devtools_Script, + Content = DEVTOOLS_ID_PREFIX+Place_Name.Content, + Devtools = DEVTOOLS_ID_PREFIX+Place_Name.Devtools, Popup = DEVTOOLS_ID_PREFIX+Place_Name.Popup, Panel = DEVTOOLS_ID_PREFIX+Place_Name.Panel, } @@ -61,7 +62,7 @@ export function createPortMessanger< let port: chrome.runtime.Port | null = _port let forwardHandler: ((e: ForwardPayload) => void) | undefined - let listeners: {[K in any]?: ((e: AnyPayload) => void)[]} = {} + let listeners: {[K in any]?: ((e: any) => void)[]} = {} if (LOG_MESSAGES) log(`${place_name_here} <-> ${place_name_conn} port connected.`) @@ -72,24 +73,20 @@ export function createPortMessanger< port = null }) - function onMessage(e: any) { + function onMessage(_e: any) { - if (!e || typeof e !== 'object') return - - let name = e['name'] - let details = e['details'] - - if (typeof name !== 'string') return + let e = to_message(_e) + if (!e) return if (LOG_MESSAGES) {log_message(place_name_here, place_name_conn, e)} - let arr = listeners[name] - if (arr) emit(arr, details) + let arr = listeners[e.name] + if (arr) emit(arr, e.details) let arr2 = listeners['*'] - if (arr2) emit(arr2, {name, details}) + if (arr2) emit(arr2, e) else if (forwardHandler) { - forwardHandler({name, details, forwarding: true}) + forwardHandler({name: e.name, details: e.details, forwarding: true}) } } port.onMessage.addListener(onMessage) @@ -114,7 +111,7 @@ export function createPortMessanger< let arr = listeners[name] ?? (listeners[name] = []) arr.push(handler) - + return () => (listeners[name] = arr.filter(l => l !== handler) as any) }, onForward(handler) { @@ -137,40 +134,57 @@ export type Versions = { extension: string } -export interface GeneralMessages { +export interface GeneralChannels { // client -> content -> devtools.html - Detected: DetectionState + Detected: DetectionState | null // the `string` payload is the main version Debugger_Connected: { solid: string | null client: string | null } - Versions: Versions + Versions: Versions | null /** devtools -> client: the chrome devtools got opened or entirely closed */ - DevtoolsOpened: void - DevtoolsClosed: void + DevtoolsOpened: boolean ResetPanel: void } +export type Channels = debug.Debugger.InputChannels + & debug.Debugger.OutputChannels + & GeneralChannels + +export type Message = { + [K in keyof Channels]: { + name: K, + details: Channels[K], + forwarding?: boolean, + } +}[keyof Channels] + +export function to_message(e: any): Message | null { + return e && typeof e === 'object' && typeof e['name'] === 'string' + ? e + : null +} + export type PostMessageFn = Record> = < - K extends keyof (GeneralMessages & M), + K extends keyof (GeneralChannels & M), >( type: K, - ..._: void extends (GeneralMessages & M)[K] - ? [payload?: (GeneralMessages & M)[K]] - : [payload: (GeneralMessages & M)[K]] + ..._: void extends (GeneralChannels & M)[K] + ? [payload?: (GeneralChannels & M)[K]] + : [payload: (GeneralChannels & M)[K]] ) => void export type OnMessageFn = Record> = { - ( + ( name: K, - handler: (payload: (GeneralMessages & M)[K]) => void, + handler: (payload: (GeneralChannels & M)[K]) => void, ): VoidFunction - ( - handler: (e: {name: K; details: (GeneralMessages & M)[K]}) => void, + ( + handler: (e: {name: K; details: (GeneralChannels & M)[K]}) => void, ): VoidFunction } @@ -179,29 +193,25 @@ export const makePostMessage: >() => PostMessageFn postMessage({name, details}, '*') -const window_listeners: {[K in any]?: ((e: AnyPayload) => void)[]} = {} +const window_listeners: {[K in any]?: ((e: any) => void)[]} = {} export function makeMessageListener > (place_name: Place_Name): OnMessageFn { - addEventListener('message', e => { - - if (!e.data || typeof e.data !== 'object') return - - let name = e.data['name'] - let details = e.data['details'] + addEventListener('message', _e => { - if (typeof name !== 'string') return + let e = to_message(_e.data) + if (!e) return - if (LOG_MESSAGES) {log_message(place_name, 'Window', e.data)} + if (LOG_MESSAGES) {log_message(place_name, 'Window', e)} - let arr = window_listeners[name] - if (arr) emit(arr, details) + let arr = window_listeners[e.name] + if (arr) emit(arr, e.details) let arr2 = window_listeners['*'] - if (arr2) emit(arr2, {name, details}) + if (arr2) emit(arr2, e) }) return (...args: [any, any] | [any]) => { @@ -226,10 +236,10 @@ export const forwardMessageToWindow = (message: ForwardPayload) => { postMessage({name: message.name, details: message.details}, '*') } -export function once, K extends keyof (GeneralMessages & M)>( +export function once, K extends keyof (GeneralChannels & M)>( method: OnMessageFn, name: K, - handler: (details: (GeneralMessages & M)[K]) => void, + handler: (details: (GeneralChannels & M)[K]) => void, ): VoidFunction { const unsub = method(name, (...cbArgs) => { unsub() diff --git a/packages/extension/src/content.ts b/packages/extension/src/content.ts index 3854489e..efaa2a50 100644 --- a/packages/extension/src/content.ts +++ b/packages/extension/src/content.ts @@ -17,7 +17,7 @@ import detectorPath from './detector.ts?script&module' // @ts-expect-error ?script&module query ensures output in ES module format and only import the script path import debuggerPath from './debugger.ts?script&module' -if (import.meta.env.DEV) log(bridge.Place_Name.Content_Script+' loaded.') +if (import.meta.env.DEV) log(bridge.Place_Name.Content+' loaded.') const extension_version = chrome.runtime.getManifest().version @@ -25,11 +25,11 @@ const port = chrome.runtime.connect({name: bridge.ConnectionName.Content}) let devtools_opened = false -const fromClient = bridge.makeMessageListener(bridge.Place_Name.Content_Script) +const fromClient = bridge.makeMessageListener(bridge.Place_Name.Content) const toClient = bridge.makePostMessage() const bg_messanger = bridge.createPortMessanger( - bridge.Place_Name.Content_Script, + bridge.Place_Name.Content, bridge.Place_Name.Background, port) @@ -102,17 +102,16 @@ fromClient('Debugger_Connected', versions => { fromClient('ResetPanel', () => bg_messanger.post('ResetPanel')) - if (devtools_opened) toClient('DevtoolsOpened') + if (devtools_opened) toClient('DevtoolsOpened', devtools_opened) }) // After page reload, the content script is reloaded but the background script is not. // This means that 'DevtoolsOpened' message will come after the Client is setup. // We need to send it after it connects. -bg_messanger.on('DevtoolsOpened', () => { - devtools_opened = true - toClient('DevtoolsOpened') +bg_messanger.on('DevtoolsOpened', opened => { + devtools_opened = opened + toClient('DevtoolsOpened', opened) }) -bg_messanger.on('DevtoolsClosed', () => toClient('DevtoolsClosed')) fromClient(e => { // forward all client messages to the background script in diff --git a/packages/extension/src/debugger.ts b/packages/extension/src/debugger.ts index c301c11f..38d95b50 100644 --- a/packages/extension/src/debugger.ts +++ b/packages/extension/src/debugger.ts @@ -86,8 +86,7 @@ toContent('Debugger_Connected', { solid: debug.meta.versions.get_solid(), }) -fromContent('DevtoolsOpened', () => debug.toggleEnabled(true)) -fromContent('DevtoolsClosed', () => debug.toggleEnabled(false)) +fromContent('DevtoolsOpened', opened => debug.toggleEnabled(opened)) // pass all the devtools events to the debugger fromContent(e => debug.emit(e.name as any, e.details)) diff --git a/packages/extension/src/devtools.ts b/packages/extension/src/devtools.ts index 4a9be4be..76f8760b 100644 --- a/packages/extension/src/devtools.ts +++ b/packages/extension/src/devtools.ts @@ -11,13 +11,13 @@ import {error, log} from '@solid-devtools/shared/utils' import * as bridge from './bridge.ts' import * as icons from './icons.ts' -log(bridge.Place_Name.Devtools_Script+' loaded.') +log(bridge.Place_Name.Devtools+' loaded.') // Create a connection to the background page const port = chrome.runtime.connect({name: bridge.ConnectionName.Devtools}) const bg_messanger = bridge.createPortMessanger( - bridge.Place_Name.Devtools_Script, + bridge.Place_Name.Devtools, bridge.Place_Name.Background, port) diff --git a/packages/extension/src/panel.tsx b/packages/extension/src/panel.tsx index 79fba773..0790e3c7 100644 --- a/packages/extension/src/panel.tsx +++ b/packages/extension/src/panel.tsx @@ -21,18 +21,30 @@ const bg_messanger = bridge.createPortMessanger port) function App() { - const [versions, setVersions] = createSignal({ - solid: '', - client: '', + const empty_versions: bridge.Versions = { + solid: '', + client: '', expectedClient: '', - extension: '', - }) + extension: '', + } + + const [versions, setVersions] = createSignal(empty_versions) - bridge.once(bg_messanger.on, 'Versions', setVersions) + bridge.once(bg_messanger.on, 'Versions', e => { + if (e) { + setVersions(e) + } else { + setVersions(empty_versions) + } + }) const devtools = createDevtools() - devtools.bridge.output.listen(e => bg_messanger.post(e.name, e.details)) + devtools.bridge.output.listen(e => bg_messanger.forward({ + name: e.name, + details: e.details, + forwarding: true, + })) bg_messanger.on(e => { // some events are internal and should not be forwarded to the devtools diff --git a/packages/extension/src/popup.tsx b/packages/extension/src/popup.tsx index 94894998..5c08b48a 100644 --- a/packages/extension/src/popup.tsx +++ b/packages/extension/src/popup.tsx @@ -17,13 +17,20 @@ const bg_messanger = bridge.createPortMessanger( port) const [versions, setVersions] = s.createSignal(null) -const [detectionState, setDetectionState] = s.createSignal({ +const empty_detection_state: bridge.DetectionState = { Solid: false, SolidDev: false, Debugger: false, -}) +} +const [detectionState, setDetectionState] = s.createSignal(empty_detection_state) -bg_messanger.on('Detected', setDetectionState) +bg_messanger.on('Detected', e => { + if (e) { + setDetectionState(e) + } else { + setDetectionState(empty_detection_state) + } +}) bg_messanger.on('Versions', setVersions) const App: s.Component = () => { diff --git a/packages/extension/vite.config.ts b/packages/extension/vite.config.ts index 5add7675..9d081906 100644 --- a/packages/extension/vite.config.ts +++ b/packages/extension/vite.config.ts @@ -106,6 +106,9 @@ const vite_config: vite.UserConfig = { }, target: 'esnext', }, + esbuild: { + dropLabels: [is_dev ? 'PROD' : 'DEV'], + }, optimizeDeps: { exclude: ['@solid-devtools/debugger'], },