Comparing version 4.2.0 to 4.3.0
{ | ||
"name": "0x", | ||
"version": "4.2.0", | ||
"version": "4.3.0", | ||
"description": "🔥 single-command flamegraph profiling 🔥", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -8,6 +8,22 @@ 'use strict' | ||
const unmergedTags = tagNodesWithIds(state.trees.unmerged) | ||
const mergedTags = tagNodesWithIds(state.trees.merged) | ||
return { | ||
search, control, zoom, typeFilters | ||
focusNode, | ||
search, control, zoom, typeFilters, | ||
pushState, jumpToState | ||
} | ||
function focusNode () { | ||
const save = pushState() | ||
return (node) => { | ||
const { merged } = state.control | ||
const { nodesToIds } = merged ? mergedTags : unmergedTags | ||
state.focusedNodeId = nodesToIds.get(node) | ||
save() | ||
} | ||
} | ||
function search () { | ||
@@ -94,2 +110,3 @@ return ({type, value}) => { | ||
function typeFilters () { | ||
const save = pushState() | ||
return ({name}) => { | ||
@@ -105,5 +122,82 @@ const checked = state.typeFilters.exclude.has(name) | ||
if (state.control.tiers) state.typeFilters.bgs = highlightTypeFilters() | ||
save() | ||
emit(state) | ||
} | ||
} | ||
function pushState () { | ||
return () => { | ||
const { merged } = state.control | ||
const excludeTypes = Array.from(state.typeFilters.exclude) | ||
const historyState = { | ||
merged, | ||
nodeId: state.focusedNodeId, | ||
excludeTypes | ||
} | ||
window.history.pushState(historyState, '', `#${stringifyHistoryState(historyState)}`) | ||
} | ||
} | ||
// Jump to a state based on a history entry. | ||
function jumpToState () { | ||
return ({merged, nodeId, excludeTypes}) => { | ||
state.focusedNodeId = nodeId | ||
state.control.merged = merged | ||
state.typeFilters.enableInlinable = !merged | ||
state.key.enableOptUnopt = !merged | ||
// Diff type exclude state to reach the one described by the entry | ||
const oldExclude = state.typeFilters.exclude | ||
const newExclude = new Set(excludeTypes) | ||
oldExclude.forEach((name) => { | ||
if (!newExclude.has(name)) { | ||
flamegraph.typeShow(name) | ||
} | ||
}) | ||
newExclude.forEach((name) => { | ||
if (!oldExclude.has(name)) { | ||
flamegraph.typeHide(name) | ||
} | ||
}) | ||
state.typeFilters.exclude = newExclude | ||
state.typeFilters.bgs = state.control.tiers | ||
? highlightTypeFilters() | ||
: state.typeFilters.unhighlighted | ||
flamegraph.renderTree(merged ? state.trees.merged : state.trees.unmerged) | ||
const { idsToNodes } = merged ? mergedTags : unmergedTags | ||
flamegraph.zoom(idsToNodes.get(nodeId)) | ||
emit(state) | ||
} | ||
} | ||
} | ||
// This just uses incrementing IDs but it will only | ||
// be used for a single dataset, and it's deterministic enough for that | ||
function tagNodesWithIds (data) { | ||
let id = 0 | ||
const idsToNodes = new Map() | ||
const nodesToIds = new Map() | ||
tagNodes(data) | ||
return { | ||
idsToNodes, | ||
nodesToIds | ||
} | ||
function tag (node) { | ||
idsToNodes.set(id, node) | ||
nodesToIds.set(node, id) | ||
id++ | ||
} | ||
function tagNodes (node) { | ||
tag(node) | ||
if (node.children) node.children.forEach(tagNodes) | ||
} | ||
} | ||
function stringifyHistoryState (state) { | ||
// Just use JSON I guess | ||
return JSON.stringify(state) | ||
} |
'use strict' | ||
module.exports = (render) => Object.assign(() => render` | ||
<chart | ||
class='db overflow-y-scroll overflow-x-hidden relative' | ||
<chart | ||
class='db overflow-y-scroll overflow-x-hidden relative' | ||
style='padding-left: 5%; padding-right: 5%; height: calc(100vh - 66px)' | ||
@@ -14,27 +14,23 @@ > | ||
if (/\[INLINABLE\]/.test(name)) return {type: 'inlinable'} | ||
if (/\[INIT]$/.test(name)) return {type: 'init'} | ||
if (!/\.js/.test(name)) { | ||
switch (true) { | ||
case /\[CODE:RegExp\]/.test(name): return {type: 'regexp'} | ||
case /\[CODE:.*\]/.test(name): return {type: 'v8'} | ||
case /\.$/.test(name): return {type: 'core'} | ||
case /\[CPP\]/.test(name): return {type: 'cpp'} | ||
case /\[SHARED_LIB\]/.test(name): return {type: 'cpp'} | ||
case /\[eval\]/.test(name): return {type: 'native'} // unless we create an eval checkbox | ||
if (/\[INLINABLE]$/.test(name)) return {type: 'inlinable'} | ||
if (!/\.m?js/.test(name)) { | ||
if (/\[CODE:RegExp]$/.test(name)) return {type: 'regexp'} | ||
if (/\[CODE:.*?]$/.test(name) || /v8::internal::.*\[CPP]$/.test(name)) return {type: 'v8'} | ||
if (/\.$/.test(name)) return {type: 'core'} | ||
if (/\[CPP]$/.test(name) || /\[SHARED_LIB]$/.test(name)) return {type: 'cpp'} | ||
if (/\[eval]/.test(name)) return {type: 'native'} // unless we create an eval checkbox | ||
// "native" is the next best label since | ||
// you cannot tell where the eval comes | ||
// from (app, deps, core) | ||
default: return {type: 'v8'} | ||
} | ||
return {type: 'v8'} | ||
} | ||
if (/\[INIT\]/.test(name)) return {type: 'init'} | ||
if (/ native /.test(name)) return {type: 'native'} | ||
if (name.indexOf('/') === -1 || (/internal\//.test(name) && !/ \//.test(name))) return {type: 'core'} | ||
if (/node_modules/.test(name)) return {type: 'deps'} | ||
switch (true) { | ||
case / native /.test(name): return {type: 'native'} | ||
case (name.indexOf('/') === -1 || (/internal\//.test(name) && !/ \//.test(name))): return {type: 'core'} | ||
case /node_modules/.test(name): return {type: 'deps'} | ||
default: return {type: 'app'} | ||
} | ||
return {type: 'app'} | ||
} |
@@ -28,2 +28,21 @@ 'use strict' | ||
let userZoom = true // false if the last zoom call was initiated by 0x | ||
flamegraph.on('zoom', (d) => { | ||
if (!userZoom) { | ||
userZoom = true | ||
return | ||
} | ||
focusNode(d) | ||
}) | ||
window.addEventListener('popstate', (event) => { | ||
userZoom = false | ||
jumpToState(event.state || { | ||
// No hash anymore, jump to root node (0) but don't change settings | ||
merged: state.control.merged, | ||
excludeTypes: Array.from(state.typeFilters.exclude), | ||
nodeId: 0 | ||
}) | ||
}) | ||
window.addEventListener('resize', debounce(() => { | ||
@@ -40,5 +59,24 @@ const width = document.body.clientWidth * 0.89 | ||
const iface = ui({state, actions}) | ||
const focusNode = actions.focusNode() | ||
const jumpToState = actions.jumpToState() | ||
document.body.appendChild(chart) | ||
document.body.appendChild(iface) | ||
if (window.location.hash) { | ||
const st = parseHistoryState(window.location.hash.slice(1)) | ||
if (st) { | ||
userZoom = false | ||
jumpToState(st) | ||
} | ||
} | ||
} | ||
function parseHistoryState (str) { | ||
try { | ||
return JSON.parse(decodeURIComponent(str)) | ||
} catch (err) { | ||
// Just ignore if someone used an incorrect hash | ||
return null | ||
} | ||
} |
@@ -8,2 +8,3 @@ 'use strict' | ||
trees, | ||
focusedNodeId: null, | ||
key: { | ||
@@ -10,0 +11,0 @@ colors: [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
87555
1750
10
7
39