├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── inspect ├── Echo.svelte ├── Formatter.svelte ├── Getter.svelte ├── Inspect.svelte ├── PrimitiveBase.svelte ├── Property.svelte ├── PropertyList.svelte ├── Toggle.svelte ├── focus-actions.js ├── formatters │ ├── ArrayFormatter.svelte │ ├── BigIntFormatter.svelte │ ├── BooleanFormatter.svelte │ ├── DateFormatter.svelte │ ├── ElementFormatter.svelte │ ├── ErrorFormatter.svelte │ ├── FallbackFormatter.svelte │ ├── FunctionFormatter.svelte │ ├── MapEntry.svelte │ ├── MapFormatter.svelte │ ├── NilFormatter.svelte │ ├── NumberFormatter.svelte │ ├── ObjectFormatter.svelte │ ├── RegExpFormatter.svelte │ ├── SetFormatter.svelte │ ├── StringFormatter.svelte │ ├── SymbolFormatter.svelte │ └── TypedArrayFormatter.svelte └── utilities.js ├── package.json └── svelte-inspect.js /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | 7 | indent_style = space 8 | indent_size = 2 9 | 10 | root = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | public/bundle.* 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.3 4 | - Fix repeated slot names, which are no longer supported by Svelte. 5 | 6 | ## 0.1.2 7 | - Support configuration even in SSR mode. 8 | - Clean up MapEntry.svelte to avoid unused CSS warning. 9 | 10 | ## 0.1.1 11 | - Allow logging a single value with ``, without formatting as a variable. 12 | - Print sequences of whitespace correctly in strings, object keys, regex source, element attribute values and Symbol keys 13 | - Add formatters for Map and Set 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 trbrc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte Inspect 2 | 3 | Live & interactive object inspector for [Svelte](https://svelte.dev), inspired by DevTools. 4 | 5 | ```console 6 | npm install --save svelte-inspect 7 | ``` 8 | 9 | Try the example in the [svelte.dev REPL](https://svelte.dev/repl/eb3b3ae5639544d78d7363e126b29896). Use mouse and keyboard to inspect the todos. 10 | 11 | ```html 12 | 25 | 26 | 27 | 28 | 29 | 30 | ``` 31 | 32 | ## Types 33 | 34 | See [REPL demo of most of the types](https://svelte.dev/repl/06f3a93da3454c6982aa5a38c541a5b0). These types have special formatting: 35 | 36 | - Arrays (including TypedArrays) 37 | - Objects 38 | - Functions (including async functions) 39 | - Classes 40 | - Map and Set 41 | - RegExps 42 | - Dates 43 | - Booleans 44 | - undefined and null 45 | - Numbers (including BigInt) 46 | - Strings 47 | - Symbols 48 | - Errors 49 | - HTML elements 50 | 51 | There's support for enumerable and non-enumerable properties, symbol keys, `__proto__`, and getters (click to evaluate). It does not yet have any special support for e.g. iterators, promises. 52 | 53 | ## Keyboard navigation 54 | 55 | You can use your keyboard to move around in the object hierarchy. 56 | 57 | Keys | | Action 58 | :--- | :--- | :--- 59 | AZ 09 | Type anything | Jump using fuzzy matching 60 | | Tab | Focus next 61 | | Shift + Tab | Focus previous 62 | | Left | Close or step out 63 | | Right | Open or step in 64 | | Up | Focus previous 65 | | Down | Focus next 66 |       | Space | Toggle 67 | | Enter | Open & step in 68 | esc | Escape | Step out 69 | 70 | ## API 71 | 72 | ### `Inspect` 73 | 74 | Create one inspector for every value passed in as a prop, with default colors. 75 | 76 | ```html 77 | 80 | 81 | 82 | ``` 83 | 84 | ### `Inspect.Value` 85 | 86 | Inspect a single `value`, without showing the name of the prop. Also takes an optional `depth` directly as a prop. 87 | 88 | ```html 89 | 96 | 97 | 98 | ``` 99 | 100 | ### `Inspect.Inverted` 101 | 102 | Inspector with color palette suitable for dark backgrounds. 103 | 104 | ```html 105 | 112 | 113 | 114 | ``` 115 | 116 | ### `Inspect[0-10]` 117 | 118 | Inspectors pre-configured with `{depth: 0-10}`. 119 | 120 | ```html 121 | 124 | 125 | 126 | ``` 127 | 128 | ### `Inspect.configure(configuration)` 129 | 130 | Create an inspect component with custom configuration. 131 | 132 | ```html 133 | 142 | 143 | 144 | ``` 145 | 146 | Note the use of [`context="module"`](https://svelte.dev/docs#script_context_module), which is required for the Svelte compiler to understand that `CustomInspector` can be used in the template. 147 | 148 | The config is WIP. There are currently two options: 149 | 150 | #### `{depth: number}` 151 | 152 | Set how many levels of the object hierarchy should start in the open state. Defaults to `1`. 153 | 154 | ```html 155 | 164 | 165 | 166 | 167 | ``` 168 | 169 | Non-enumerable properties will not be opened. 170 | 171 | #### `{palette: {...colors}}` 172 | 173 | Create a component with a customized color palette. Values are any valid CSS color, keys are `red`, `blue`, `green`, `purple`, `orange`, `yellow`, `brown`, `pink`, `gray`, `black`, `white` and `selection` (note that not all of these colors are currently used). 174 | 175 | *(work in progress; expect to change)* 176 | 177 | ```html 178 | 187 | ``` 188 | -------------------------------------------------------------------------------- /inspect/Echo.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /inspect/Formatter.svelte: -------------------------------------------------------------------------------- 1 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /inspect/Getter.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | {#if hasGottenResult} 29 | 30 | get 31 | 32 | 33 | {:else} 34 | 38 | get 39 | 40 | {#if hasError} 41 | {getterError} 42 | {:else} 43 | () 44 | {/if} 45 | 46 | {/if} 47 | 48 | 65 | -------------------------------------------------------------------------------- /inspect/Inspect.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | {#if valueOnly} 26 | 27 | 31 | 32 | {:else} 33 | {#each propKeys as key} 34 | 35 | 42 | 43 | {/each} 44 | {/if} 45 | 46 | 86 | -------------------------------------------------------------------------------- /inspect/PrimitiveBase.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | {#if !isOpen} 15 | 16 | {:else} 17 | { 18 | {/if} 19 | 20 | 21 | {#if isOpen} 22 | 25 | } 26 | {/if} 27 | 28 | 37 | -------------------------------------------------------------------------------- /inspect/Property.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | {String(key)}{separator} 24 | 25 | 26 | 27 | 44 | -------------------------------------------------------------------------------- /inspect/PropertyList.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | {#each properties.slice(0, maxCount) as property (property)} 20 |
21 |
  
22 | 23 | 30 | 31 |
32 | {:else} 33 |
34 |
  
35 | No properties 36 |
37 | {/each} 38 | {#if maxCount < properties.length} 39 | {#each {length: 1} as _ (maxCount)} 40 |
41 |
  
42 | { 44 | maxCount += paging; 45 | focusPrev(); 46 | onTick(focusNext); 47 | }} 48 | > 49 | Show {maxCount} … {Math.min(properties.length, maxCount + paging - 1)} of {properties.length} properties 50 | 51 |
52 | {/each} 53 | {/if} 54 |
55 | 56 | 76 | -------------------------------------------------------------------------------- /inspect/Toggle.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 98 | 99 | { 103 | if (event.detail === 1) { 104 | toggle(); 105 | } else if (isOpen && event.detail === 2) { 106 | enterFocusScope(); 107 | } 108 | }} 109 | on:keydown={onKeyDown} 110 | class:toggle={true} 111 | class={className} 112 | > 113 | 114 | 115 | 116 | 128 | -------------------------------------------------------------------------------- /inspect/focus-actions.js: -------------------------------------------------------------------------------- 1 | let serialCounter = 0; 2 | 3 | export function target(element) { 4 | // This should probably not be a global serial, but based instead on the scope. 5 | element.dataset.focusTarget = serialCounter++; 6 | element.tabIndex = 0; 7 | const focusEventListener = () => { 8 | setFocus(element); 9 | }; 10 | element.addEventListener('focus', focusEventListener); 11 | return { 12 | destroy() { 13 | element.removeEventListener('focus', focusEventListener); 14 | } 15 | }; 16 | } 17 | 18 | export function scope(element) { 19 | element.dataset.focusScope = ''; 20 | } 21 | 22 | const createRangeFrom = element => { 23 | const range = document.createRange(); 24 | range.selectNodeContents(element); 25 | return range; 26 | }; 27 | 28 | function getElementBefore(referenceElement, elements) { 29 | const range = createRangeFrom(referenceElement); 30 | const lastIndex = elements.length - 1; 31 | for (let i = lastIndex; i >= 0; i--) { 32 | if (range.comparePoint(elements[i], 0) === -1) { 33 | return elements[i]; 34 | } 35 | } 36 | return elements[lastIndex]; 37 | } 38 | 39 | function getElementAfter(referenceElement, elements) { 40 | const range = createRangeFrom(referenceElement); 41 | for (let i = 0; i < elements.length; i++) { 42 | if (range.comparePoint(elements[i], 0) === 1) { 43 | return elements[i]; 44 | } 45 | } 46 | return elements[0]; 47 | } 48 | 49 | function getTargetBefore(element) { 50 | return getElementBefore( 51 | element, 52 | document.querySelectorAll('[data-focus-target]') 53 | ); 54 | } 55 | 56 | function getTargetAfter(element) { 57 | return getElementAfter( 58 | element, 59 | document.querySelectorAll('[data-focus-target]') 60 | ); 61 | } 62 | 63 | function getParentScope(element) { 64 | return element.closest('[data-focus-scope]'); 65 | } 66 | 67 | function setFocus(targetElement) { 68 | if (targetElement) { 69 | const serial = targetElement.dataset.focusTarget; 70 | if (serial) { 71 | const scope = getParentScope(targetElement); 72 | if (scope) { 73 | scope.dataset.focusScope = serial; 74 | } 75 | } 76 | targetElement.focus(); 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | export function focusPrev() { 83 | return setFocus(getTargetBefore(document.activeElement)); 84 | } 85 | 86 | export function focusNext() { 87 | return setFocus(getTargetAfter(document.activeElement)); 88 | } 89 | 90 | export function exitFocusScope() { 91 | const parent = getParentScope(document.activeElement); 92 | if (parent) { 93 | return setFocus(getTargetBefore(parent)); 94 | } else { 95 | document.activeElement.blur(); 96 | return false; 97 | } 98 | } 99 | 100 | export function enterFocusScope() { 101 | const nextScope = getElementAfter( 102 | document.activeElement, 103 | document.querySelectorAll('[data-focus-scope]') 104 | ); 105 | const targetBefore = getTargetBefore(nextScope); 106 | if (targetBefore !== document.activeElement) { 107 | // We only want to enter the scope if it is directly after activeElement 108 | return false; 109 | } 110 | const serial = nextScope.dataset.focusScope; 111 | const selector = serial 112 | ? `[data-focus-target="${serial}"]` 113 | : `[data-focus-target]`; 114 | const target = nextScope.querySelector(selector); 115 | return setFocus(target); 116 | } 117 | 118 | export function focusBySearch(string) { 119 | // Find a focus target that matches the string. 120 | try { 121 | const regex = new RegExp(string.split('').join('[^a-z0-9]*.?[^a-z0-9]*'), 'i'); 122 | let scope = (document.activeElement); 123 | do { 124 | scope = getParentScope(scope.parentElement); 125 | const targets = (scope || document).querySelectorAll('[data-focus-target]'); 126 | for (let i = 0; i < targets.length; i++) { 127 | const target = targets[i]; 128 | const text = target.textContent; 129 | if (regex.test(text)) { 130 | return setFocus(target); 131 | } 132 | } 133 | } while (scope); 134 | } catch (e) {} 135 | return false; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /inspect/formatters/ArrayFormatter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | Array( 21 | {value.length} 22 | ) 23 | 24 | [{#if !isOpen} 25 | ] 26 | {/if} 27 | 28 | 29 | {#if isOpen} 30 | 34 | Empty array 35 | 36 | ] 37 | {/if} 38 | 39 | 54 | -------------------------------------------------------------------------------- /inspect/formatters/BigIntFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | 15 | {#each stringified.split(/(\d+)/) as group, index} 16 | {#if group.length} 17 | {group} 18 | {/if} 19 | {/each} 20 | 21 | 22 | 30 | -------------------------------------------------------------------------------- /inspect/formatters/BooleanFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 13 | {String(value)} 14 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /inspect/formatters/DateFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 22 | 23 | 24 | Date( 25 | 26 | {#each grouper(date) as {key, match}} 27 | {match} 28 | {/each} 29 | 30 | 31 | {#each grouper(time) as {key, match}} 32 | {match} 33 | {/each} 34 | 35 | ) 36 | 37 | 38 | 39 | 50 | -------------------------------------------------------------------------------- /inspect/formatters/ElementFormatter.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | < 26 | {value.tagName.toLowerCase()} 27 | 28 | {#each value.attributes as attribute} 29 | {' '} 30 | {#if attribute.value} 31 | 32 | {attribute.name} 33 | =" 34 | {attribute.value} 35 | " 36 | 37 | {:else} 38 | 39 | {attribute.name} 40 | 41 | {/if} 42 | {/each} 43 | 44 | > 45 | 46 | 47 | 48 | 63 | -------------------------------------------------------------------------------- /inspect/formatters/ErrorFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 13 | {String(value)} 14 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /inspect/formatters/FallbackFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 13 | {String(value)} 14 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /inspect/formatters/FunctionFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 22 | 23 | 24 | {#each grouper(functionString) as {key, match}} 25 | {match} 26 | {/each} 27 | 28 | 29 | 30 | 47 | -------------------------------------------------------------------------------- /inspect/formatters/MapEntry.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | {#if !isOpen} 17 | {String(key)} => {String(value)} 18 | {/if} 19 | 20 | 21 | {#if isOpen} 22 | { 23 | 27 | } 28 | {/if} 29 | 30 | 39 | -------------------------------------------------------------------------------- /inspect/formatters/MapFormatter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 21 | 22 | 23 | Map( 24 | {value.size} 25 | ) 26 | 27 | {{#if !isOpen} 28 | } 29 | {/if} 30 | 31 | 32 | {#if isOpen} 33 | {#each [...value] as [entryKey, entryValue], index (entryKey)} 34 |
35 |
  
36 | 37 | entry {index}: 38 | 39 |
40 | {:else} 41 |
42 |
  
43 | No entries 44 |
45 | {/each} 46 | 50 | No properties 51 | 52 | } 53 | {/if} 54 | 55 | 91 | -------------------------------------------------------------------------------- /inspect/formatters/NilFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 13 | {String(value)} 14 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /inspect/formatters/NumberFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | 15 | {#each stringified.split(/(\d+)/) as group, index} 16 | {#if group.length} 17 | {group} 18 | {/if} 19 | {/each} 20 | 21 | 22 | 30 | -------------------------------------------------------------------------------- /inspect/formatters/ObjectFormatter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 21 | 22 | {typeDescription} 23 | {{#if !isOpen} 24 | } 25 | {/if} 26 | 27 | 28 | {#if isOpen} 29 | 33 | } 34 | {/if} 35 | 36 | 48 | -------------------------------------------------------------------------------- /inspect/formatters/RegExpFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | / 20 | {source} 21 | / 22 | {flags} 23 | 24 | 25 | 26 | 27 | 39 | -------------------------------------------------------------------------------- /inspect/formatters/SetFormatter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 21 | 22 | 23 | Set( 24 | {value.size} 25 | ) 26 | 27 | {{#if !isOpen} 28 | } 29 | {/if} 30 | 31 | 32 | {#if isOpen} 33 | {#each [...value] as entryValue, index (entryValue)} 34 |
35 |
  
36 | 37 | entry {index}: 38 | 39 |
40 | {:else} 41 |
42 |
  
43 | No entries 44 |
45 | {/each} 46 | 50 | No properties 51 | 52 | } 53 | {/if} 54 | 55 | 91 | -------------------------------------------------------------------------------- /inspect/formatters/StringFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 16 | 17 | 18 | {#each groups as group} 19 | {group} 23 | {/each} 24 | 25 | 26 | 27 | 41 | -------------------------------------------------------------------------------- /inspect/formatters/SymbolFormatter.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 17 | 18 | 19 | {prefix} 20 | {key} 21 | ) 22 | 23 | 24 | 25 | 34 | -------------------------------------------------------------------------------- /inspect/formatters/TypedArrayFormatter.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 37 | 38 | 39 | 40 | 41 | {typeDescription}( 42 | {value.length} 43 | ) 44 | 45 | [{#if !isOpen} 46 | ] 47 | {/if} 48 | 49 | 50 | {#if isOpen} 51 | 54 | Empty array 55 | 56 | ] 57 | {/if} 58 | 59 | 74 | -------------------------------------------------------------------------------- /inspect/utilities.js: -------------------------------------------------------------------------------- 1 | export const isNil = (value = null) => value === null; 2 | 3 | export const keyIsIndex = key => String(parseInt(String(key))) === String(key); 4 | 5 | import {tick} from 'svelte'; 6 | 7 | export const onTick = callback => tick().then(callback); 8 | 9 | export const getObjectToStringTag = object => Object.prototype.toString.call(object).slice(8, -1); 10 | 11 | export const getObjectTypeString = object => { 12 | const hasToStringTag = Symbol.toStringTag in object; 13 | if (hasToStringTag) { 14 | return getObjectToStringTag(object); 15 | } 16 | 17 | const prototype = Object.getPrototypeOf(object); 18 | const constructorName = prototype && prototype.constructor && prototype.constructor.name; 19 | const usefulConstructorName = constructorName && constructorName !== 'Object'; 20 | 21 | return usefulConstructorName ? constructorName : getObjectToStringTag(object); 22 | }; 23 | 24 | export const getAllProperties = object => { 25 | const enumerableKeys = []; 26 | for (const enumerableKey in object) { 27 | enumerableKeys.push(enumerableKey); 28 | } 29 | return [...new Set([ 30 | ...enumerableKeys, 31 | ...Object.getOwnPropertyNames(object), 32 | ...Object.getOwnPropertySymbols(object), 33 | ...(object['__proto__'] ? ['__proto__'] : []) 34 | ])]; 35 | }; 36 | 37 | export const getPropertyDescriptor = (object, prop) => { 38 | if (!object) { 39 | return {}; 40 | } else if (prop === '__proto__') { 41 | return { 42 | value: Object.getPrototypeOf(object) 43 | }; 44 | } else { 45 | const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(object, prop); 46 | if (ownPropertyDescriptor) { 47 | return ownPropertyDescriptor; 48 | } else { 49 | const prototype = Object.getPrototypeOf(object); 50 | return getPropertyDescriptor(prototype, prop); 51 | } 52 | } 53 | }; 54 | 55 | export const stringEscapeGroups = string => { 56 | // Split string into array of sequences of characters, with needed escapes broken out 57 | const characters = string.split(/([\x00-\x1F]+)/); 58 | return characters.map(char => JSON.stringify(char).slice(1, -1)); 59 | }; 60 | 61 | const backslashCharCode = '\\'.charCodeAt(0); 62 | 63 | export const isEscapeGroup = string => string.charCodeAt(0) === backslashCharCode; 64 | 65 | export const groupByRegexes = (regexes) => { 66 | const keys = Object.keys(regexes); 67 | return string => { 68 | const groups = []; 69 | let source = string; 70 | while (source.length) { 71 | let bestKey = null; 72 | let bestMatch = null; 73 | let bestIndex = source.length; 74 | for (const key of keys) { 75 | const regex = regexes[key]; 76 | const match = source.match(regex); 77 | if (match && match.index < bestIndex) { 78 | bestKey = key; 79 | bestMatch = match[0]; 80 | bestIndex = match.index; 81 | } 82 | } 83 | if (bestIndex > 0) { 84 | groups.push({key: undefined, match: source.slice(0, bestIndex)}) 85 | } 86 | if (bestMatch) { 87 | groups.push({key: bestKey, match: bestMatch}) 88 | source = source.slice(bestIndex + bestMatch.length); 89 | } else { 90 | break; 91 | } 92 | } 93 | return groups; 94 | }; 95 | }; 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-inspect", 3 | "version": "0.1.3", 4 | "description": "Svelte object inspector", 5 | "svelte": "svelte-inspect.js", 6 | "repository": "github:trbrc/svelte-inspect", 7 | "homepage": "https://github.com/trbrc/svelte-inspect", 8 | "bugs": "https://github.com/trbrc/svelte-inspect/issues", 9 | "keywords": [ 10 | "svelte", 11 | "devtools" 12 | ], 13 | "peerDependencies": { 14 | "svelte": "^3.0.0" 15 | }, 16 | "license": "MIT" 17 | } 18 | -------------------------------------------------------------------------------- /svelte-inspect.js: -------------------------------------------------------------------------------- 1 | import SvelteInspect from './inspect/Inspect.svelte'; 2 | 3 | function configure(configuration) { 4 | if (SvelteInspect.render) { 5 | // It's an SSR'd component. We need to wrap .render 6 | return { 7 | ...SvelteInspect, 8 | render(props) { 9 | return SvelteInspect.render({ 10 | ...props, 11 | [Symbol.for('configuration')]: configuration 12 | }) 13 | } 14 | }; 15 | } else { 16 | // It's a client side component. We need to extend the class. 17 | return class ConfiguredInspect extends SvelteInspect { 18 | constructor(options) { 19 | const props = options.props || {}; 20 | super({ 21 | ...options, 22 | props: { 23 | ...props, 24 | [Symbol.for('configuration')]: configuration 25 | } 26 | }); 27 | } 28 | }; 29 | } 30 | }; 31 | 32 | const invertedPalette = { 33 | red: 'red', 34 | blue: 'dodgerblue', 35 | green: 'yellowgreen', 36 | purple: 'violet', 37 | gray: '#808080', 38 | black: '#d0d0d0', 39 | white: '#202020', 40 | selection: 'darkblue' 41 | }; 42 | 43 | const Inverted = configure({ 44 | palette: invertedPalette 45 | }); 46 | 47 | const Value = configure({ 48 | valueOnly: true 49 | }); 50 | 51 | SvelteInspect.configure = configure; 52 | SvelteInspect.Inverted = Inverted; 53 | SvelteInspect.Value = Value; 54 | 55 | for (let i = 0; i <= 10; i++) { 56 | SvelteInspect[i] = configure({depth: i}); 57 | SvelteInspect.Inverted[i] = configure({depth: i, palette: invertedPalette}); 58 | } 59 | 60 | export default SvelteInspect; 61 | 62 | export {configure, Inverted, Value}; 63 | --------------------------------------------------------------------------------