UNPKG

devtron

Version:
1,849 lines (1,535 loc) 965 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict' const Chrome = require('./chrome-helpers') const Eval = require('./eval') const View = require('./view') const metadata = require('../package') class AboutView extends View { constructor () { super('about-view') this.handleEvents() this.apis = Chrome.getChromeAPIs() this.render() } render () { this.versionLabel.textContent = metadata.version this.tabID.textContent = window.chrome.devtools.inspectedWindow.tabId if (window.chrome.runtime) { this.runtimeID.textContent = window.chrome.runtime.id } this.chromeAPIs.textContent = this.apis.sort().join('\n') } handleEvents () { this.issueButton.addEventListener('click', () => this.reportIssue()) } reportIssue () { Eval.openExternal('https://github.com/electron/devtron/issues') } } module.exports = AboutView },{"../package":189,"./chrome-helpers":7,"./eval":9,"./view":28}],2:[function(require,module,exports){ 'use strict' const SelectableView = require('./selectable-view') const Eval = require('./eval') class AccessibilityElementView extends SelectableView { constructor (element, parent) { super('element-row') this.path = element.selector this.pathId = element.id parent.appendChild(this.element) this.render() this.handleEvents(parent) } handleEvents (parent) { this.listenForSelection(parent) this.listenForSelectionKeys(parent.parentElement) } render () { this.selectorPath.textContent = this.path // Add a click-handler that will select the element. // Uses the `accessibilityAuditMap` defined in accessibility.js this.selectorPath.onclick = (evt) => { evt.stopPropagation() Eval.execute(`inspect(window.__devtron.accessibilityAuditMap.get(${this.pathId}))`) } } filter (searchText) { let matches = this.path.toLowerCase().includes(searchText) matches ? this.show() : this.hide() return matches } } module.exports = AccessibilityElementView },{"./eval":9,"./selectable-view":26}],3:[function(require,module,exports){ 'use strict' const ExpandableView = require('./expandable-view') const AccessibilityElementView = require('./accessibility-element-view') class AccessibilityRuleView extends ExpandableView { constructor (rule, table) { super('rule-row') this.rule = rule this.count = rule.elements.length table.appendChild(this.element) this.handleEvents(table) this.render() this.children = rule.elements.map((element) => { return new AccessibilityElementView(element, table) }) this.collapse() } handleEvents (table) { this.listenForSelection(table) this.listenForSelectionKeys(table.parentElement) this.listenForExpanderKeys(table.parentElement) } render () { this.status.textContent = this.rule.status this.severity.textContent = this.rule.severity this.ruleName.textContent = this.rule.title this.elementCount.textContent = `(${this.count})` if (this.count === 0) { this.disclosure.style.visibility = 'hidden' } } filter (searchText) { this.collapse() let matches = this.rule.title.toLowerCase().includes(searchText) || this.rule.status.toLowerCase().includes(searchText) this.children.forEach((child) => { if (child.filter(searchText)) matches = true }) if (matches) { this.markCollapsed() this.show() this.markExpanded() } else { this.hide() } return matches } } module.exports = AccessibilityRuleView },{"./accessibility-element-view":2,"./expandable-view":14}],4:[function(require,module,exports){ 'use strict' const Eval = require('./eval') const AccessibilityRuleView = require('./accessibility-rule-view') const View = require('./view') const accessibility = require('./accessibility') class AccessibilityView extends View { constructor () { super('accessibility-view') this.handleEvents() } handleEvents () { this.debounceInput(this.searchBox, () => this.filterAudits()) this.docsButton.addEventListener('click', () => this.openDocs()) this.accessibilityButton.addEventListener('click', () => this.audit()) } audit () { accessibility.audit().then((results) => { return this.render(results) }) } render (results) { this.tableDescription.classList.add('hidden') this.accessibilityResultsTable.innerHTML = '' this.children = results.map((result) => { return new AccessibilityRuleView(result, this.accessibilityResultsTable) }) this.children[0].select() } openDocs () { Eval.openExternal('https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules') } filterAudits () { const searchText = this.getFilterText() if (searchText) { this.children.forEach((child) => child.filter(searchText)) } else { this.children.forEach((child) => { child.show() child.collapse() }) } } getFilterText () { return this.searchBox.value.toLowerCase() } } module.exports = AccessibilityView },{"./accessibility":5,"./accessibility-rule-view":3,"./eval":9,"./view":28}],5:[function(require,module,exports){ const Eval = require('./eval') exports.audit = () => { return Eval.execute(function () { const {axs} = window.__devtron // defined in browser-globals.js const config = new axs.AuditConfiguration({showUnsupportedRulesWarning: false}) const results = axs.Audit.run(config) // Create a lookup map so users can click on an element to inspect it let idCounter = 0 window.__devtron.accessibilityAuditMap = new Map() results.forEach(function (result) { const elements = result.elements || [] elements.forEach(function (element) { const id = idCounter++ element.__accessibilityAuditId = id window.__devtron.accessibilityAuditMap.set(id, element) }) }) return results.map(function (result) { const elements = result.elements || [] let status = 'N/A' if (result.result === 'PASS') { status = 'Pass' } else if (result.result === 'FAIL') { status = 'Fail' } return { code: result.rule.code, severity: result.rule.severity, status: status, title: result.rule.heading, url: result.rule.url, elements: elements.map(function (element) { let selector = element.tagName.toLowerCase() if (element.className) { selector += '.' + element.className.split(' ').join('.') } return { selector: selector, id: element.__accessibilityAuditId } }) } }).sort(function (resultA, resultB) { const statusA = resultA.status const statusB = resultB.status const severityA = resultA.severity const severityB = resultB.severity if (statusA === statusB) { if (severityA === severityB) { return resultB.elements.length - resultA.elements.length } if (severityA === 'Severe') return -1 if (severityB === 'Severe') return 1 if (severityA === 'Warning') return -1 if (severityB === 'Warning') return 1 } else { if (statusA === 'Fail') return -1 if (statusB === 'Fail') return 1 if (statusA === 'Pass') return -1 if (statusB === 'Pass') return 1 } }) }) } },{"./eval":9}],6:[function(require,module,exports){ // This defines globals that will be used in the browser context // (via the content_scripts definition in manifest.json) // // It is generated via `npm run-script prepublish` const axs = require('accessibility-developer-tools') window.__devtron = window.__devtron || {} window.__devtron.axs = axs },{"accessibility-developer-tools":30}],7:[function(require,module,exports){ 'use strict' const objectPrototype = Object.getPrototypeOf({}) const isCustomClass = (object, prototype) => { if (typeof object !== 'object') return false if (Array.isArray(object)) return false return prototype && prototype !== objectPrototype } const checkAPI = (apis, parent, name, value) => { const api = parent + '.' + name if (typeof value === 'object') { findChromeAPIs(apis, api, value) } else { apis[api] = true } } const findChromeAPIs = (apis, parent, object) => { for (const name in object) { checkAPI(apis, parent, name, object[name]) } const prototype = Object.getPrototypeOf(object) if (isCustomClass(object, prototype)) { Object.getOwnPropertyNames(prototype).filter((name) => { return name !== 'constructor' }).forEach((name) => { checkAPI(apis, parent, name, object[name]) }) } return Object.keys(apis) } exports.getChromeAPIs = () => findChromeAPIs({}, 'chrome', window.chrome) },{}],8:[function(require,module,exports){ 'use strict' const EventView = require('./event-view') const ExpandableView = require('./expandable-view') class EmitterView extends ExpandableView { constructor (name, listeners, table) { super('emitter-row') this.name = name this.count = Object.keys(listeners).reduce((count, name) => { return count + listeners[name].length }, 0) table.appendChild(this.element) this.render() this.handleEvents(table) this.children = Object.keys(listeners).map((name) => { return new EventView(name, listeners[name], this, table) }) this.collapse() } handleEvents (table) { this.listenForSelection(table) this.listenForSelectionKeys(table.parentElement) this.listenForExpanderKeys(table.parentElement) } render () { this.emitterName.textContent = this.name this.listenerCount.textContent = `(${this.count})` } filter (searchText) { this.collapse() let matches = this.name.includes(searchText) this.children.forEach((child) => { if (child.filter(searchText)) matches = true }) if (matches) { this.markCollapsed() this.show() this.markExpanded() } else { this.hide() } return matches } } module.exports = EmitterView },{"./event-view":12,"./expandable-view":14}],9:[function(require,module,exports){ (function (process){ 'use strict' class Eval { static execute (expression) { if (typeof expression === 'function') { expression = `(${expression})` if (arguments.length > 1) { let expressionArgs = JSON.stringify(Array.prototype.slice.call(arguments, 1)) expression += `.apply(this, ${expressionArgs})` } else { expression += '()' } } expression = ` (function () { window.__devtron = window.__devtron || {} window.__devtron.evaling = true var require = window.__devtron.require || window.require var process = window.__devtron.process || window.process try { return ${expression} } finally { window.__devtron.evaling = false } })() ` return new Promise((resolve, reject) => { window.chrome.devtools.inspectedWindow.eval(expression, (result, error) => { if (error) { if (error.isException && error.value) { let stack = error.value error = new Error(stack.split('\n')[0]) error.stack = stack } reject(error) } else { resolve(result) } }) }) } static getFileSize (path) { return Eval.execute((path) => { try { return require('fs').statSync(path).size } catch (error) { return -1 } }, path) } static openExternal (urlToOpen) { return Eval.execute((urlToOpen) => { return require('electron').shell.openExternal(urlToOpen) }, urlToOpen) } static getFileVersion (filePath) { return Eval.execute((filePath) => { if (/\/atom\.asar\/(browser|common|renderer)\//.test(filePath)) return process.versions.electron const fs = require('fs') const path = require('path') const appVersion = require('electron').remote.app.getVersion() let directory = path.dirname(filePath) while (path.basename(directory) !== 'node_modules') { try { let metadataPath = path.join(directory, 'package.json') let version = JSON.parse(fs.readFileSync(metadataPath)).version if (version) return version } catch (error) { // Ignore and continue } let nextDirectory = path.dirname(directory) if (nextDirectory === directory) break directory = nextDirectory } return appVersion }, filePath) } static isDebugMode () { return Eval.execute(() => { return process && !!process.env.DEVTRON_DEBUG_PATH }) } static isApiAvailable () { return Eval.execute(() => { return typeof process === 'object' && typeof require === 'function' }) } // Start a local http server in the currently running app that will // listen to requests sent by a browser static startServer () { return Eval.execute(() => { const path = require('path') const serverPath = path.join(process.env.DEVTRON_DEBUG_PATH, 'test', 'server.js') require(serverPath) }) } // Implement the window.chrome.devtools.inspectedWindow.eval API via // window.fetch talking to a local http server running in an opened // Electron app static proxyToServer () { window.chrome.devtools = { inspectedWindow: { eval: function (expression, callback) { window.fetch('http://localhost:3948', { body: JSON.stringify({expression: expression}), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, method: 'POST' }).then((response) => { return response.json() }).then((json) => { callback(json.result) }).catch((error) => { callback(null, error) }) } } } } } module.exports = Eval }).call(this,require('_process')) },{"_process":188,"electron":undefined,"fs":31,"path":187}],10:[function(require,module,exports){ (function (process){ 'use strict' const Eval = require('./eval') exports.getEvents = () => { return Eval.execute(() => { const formatCode = (listener) => { let lines = listener.split(/\r?\n/) if (lines.length === 1) return listener let lastLine = lines[lines.length - 1] let lastLineMatch = /^(\s+)}/.exec(lastLine) if (!lastLineMatch) return listener let whitespaceRegex = new RegExp('^' + lastLineMatch[1]) return lines.map((line) => { return line.replace(whitespaceRegex, '') }).join('\n') } const getEvents = (emitter) => { const events = {} Object.keys(emitter._events).sort().forEach((name) => { let listeners = emitter.listeners(name) if (listeners.length > 0) { events[name] = listeners.map((listener) => { return formatCode(listener.toString()) }) } }) return events } const electron = require('electron') const remote = electron.remote return { 'electron.remote.getCurrentWindow()': getEvents(remote.getCurrentWindow()), 'electron.remote.getCurrentWebContents()': getEvents(remote.getCurrentWebContents()), 'electron.remote.app': getEvents(remote.app), 'electron.remote.ipcMain': getEvents(remote.ipcMain), 'electron.ipcRenderer': getEvents(electron.ipcRenderer), 'electron.remote.process': getEvents(remote.process), 'global.process': getEvents(process) } }) } }).call(this,require('_process')) },{"./eval":9,"_process":188,"electron":undefined}],11:[function(require,module,exports){ 'use strict' const highlight = require('highlight.js') const SelectableView = require('./selectable-view') class EventListenerView extends SelectableView { constructor (listener, parent) { super('listener-code-row') this.listener = listener parent.appendChild(this.element) this.render() this.handleEvents(parent) } handleEvents (parent) { this.listenForSelection(parent) this.listenForSelectionKeys(parent.parentElement) } render () { this.listenerValue.textContent = this.listener highlight.highlightBlock(this.listenerValue) } filter (searchText) { let matches = this.listener.toLowerCase().includes(searchText) matches ? this.show() : this.hide() return matches } } module.exports = EventListenerView },{"./selectable-view":26,"highlight.js":33}],12:[function(require,module,exports){ 'use strict' const ExpandableView = require('./expandable-view') const EventListenerView = require('./event-listener-view') class EventView extends ExpandableView { constructor (name, listeners, parent, table) { super('event-type-row') this.name = name this.count = listeners.length this.parent = parent table.appendChild(this.element) this.handleEvents(table) this.render() this.children = listeners.map((listener) => { return new EventListenerView(listener, table) }) this.collapse() } handleEvents (table) { this.listenForSelection(table) this.listenForSelectionKeys(table.parentElement) this.listenForExpanderKeys(table.parentElement) } render () { this.eventName.textContent = this.name this.listenerCount.textContent = `(${this.count})` } filter (searchText) { this.collapse() let matches = this.name.includes(searchText) this.children.forEach((child) => { if (child.filter(searchText)) matches = true }) if (matches) { this.markCollapsed() this.show() this.markExpanded() } else { this.hide() } return matches } } module.exports = EventView },{"./event-listener-view":11,"./expandable-view":14}],13:[function(require,module,exports){ 'use strict' const events = require('./event-helpers') const EmitterView = require('./emitter-view') const View = require('./view') class EventsView extends View { constructor () { super('events-view') this.children = [] this.handleEvents() } reload () { this.loadEvents() } focus () { this.listenersTable.focus() } handleEvents () { this.loadButton.addEventListener('click', () => this.loadEvents()) this.debounceInput(this.searchBox, () => this.filterEvents()) } filterEvents () { const searchText = this.searchBox.value.toLowerCase() if (searchText) { this.children.forEach((child) => { child.filter(searchText) }) } else { this.children.forEach((child) => { child.show() child.collapse() }) } } loadEvents () { events.getEvents().then((events) => { this.tableDescription.classList.add('hidden') this.listenersTable.innerHTML = '' this.destroyChildren() this.children = Object.keys(events).map((name) => { return new EmitterView(name, events[name], this.listenersTable) }) this.children[0].select() }).catch((error) => { console.error('Getting event listeners failed') console.error(error.stack || error) }) } } module.exports = EventsView },{"./emitter-view":8,"./event-helpers":10,"./view":28}],14:[function(require,module,exports){ 'use strict' const SelectableView = require('./selectable-view') class ExpandableView extends SelectableView { constructor (viewId) { super(viewId) this.listenForArrowClicks() } toggleExpansion () { if (this.expanded) { this.collapse() } else { this.expand() } } markExpanded () { this.expanded = true this.disclosure.classList.add('disclosure-arrow-expanded') } expand () { this.markExpanded() this.children.forEach((child) => child.show()) } markCollapsed () { this.expanded = false this.disclosure.classList.remove('disclosure-arrow-expanded') } collapse () { this.markCollapsed() this.children.forEach((child) => child.hide()) } collapseAll () { this.collapse() this.children.forEach((child) => child.collapse()) } hide () { super.hide() this.children.forEach((child) => child.hide()) } show () { super.show() if (this.expanded) this.children.forEach((child) => child.show()) } listenForArrowClicks () { this.disclosure.addEventListener('click', () => this.toggleExpansion()) } listenForExpanderKeys (emitter) { this.bindListener(emitter, 'keydown', (event) => { if (!this.selected) return if (event.altKey || event.metaKey || event.ctrlKey) return switch (event.code) { case 'ArrowLeft': if (this.expanded) { this.collapse() } else if (this.parent && this.parent.expanded) { this.deselect() this.parent.collapse() this.parent.select() } event.stopImmediatePropagation() event.preventDefault() break case 'ArrowRight': this.expand() event.stopImmediatePropagation() event.preventDefault() break } }) } } module.exports = ExpandableView },{"./selectable-view":26}],15:[function(require,module,exports){ 'use strict' const AboutView = require('./about-view') const Eval = require('./eval') const EventsView = require('./events-view') const IpcView = require('./ipc-view') const LintView = require('./lint-view') const AccessibilityView = require('./accessibility-view') const ModulesView = require('./modules-view') const NodeIntegrationView = require('./node-integration-view') const SidebarView = require('./sidebar-view') document.addEventListener('DOMContentLoaded', () => { Eval.isApiAvailable().then(function (apiAvailable) { const sidebarView = new SidebarView() if (apiAvailable) { sidebarView.addPane(new ModulesView()) sidebarView.addPane(new EventsView()) sidebarView.addPane(new IpcView()) sidebarView.addPane(new LintView()) sidebarView.addPane(new AccessibilityView()) sidebarView.addPane(new AboutView()) listenForLinkClicks() } else { sidebarView.addPane(new NodeIntegrationView()) } }) }) if (!window.chrome.devtools) { Eval.proxyToServer() } else { Eval.isDebugMode().then(function (debugMode) { if (debugMode) Eval.startServer() }) } const listenForLinkClicks = () => { document.body.addEventListener('click', (event) => { const href = event.target.href if (href) { Eval.openExternal(href) event.stopImmediatePropagation() event.preventDefault() } }) } },{"./about-view":1,"./accessibility-view":4,"./eval":9,"./events-view":13,"./ipc-view":18,"./lint-view":20,"./modules-view":24,"./node-integration-view":25,"./sidebar-view":27}],16:[function(require,module,exports){ 'use strict' const highlight = require('highlight.js') const SelectableView = require('./selectable-view') class IpcEventView extends SelectableView { constructor (event, table) { super('ipc-table-row') this.event = event this.internalEvent = event.channel.startsWith('ELECTRON_') || event.channel.startsWith('ATOM_') table.appendChild(this.element) this.listenForSelection(table) this.listenForSelectionKeys(table.parentElement) this.render() } render () { this.eventName.textContent = this.event.channel this.eventName.title = this.event.channel if (this.event.sent) { this.eventIcon.classList.add('ipc-icon-sent') this.eventIcon.title = 'Outgoing' } else { this.eventIcon.classList.add('ipc-icon-received') this.eventIcon.title = 'Incoming' } if (!this.event.sync) { this.syncIcon.style.display = 'none' } if (this.event.listenerCount > 0) { this.eventListenerCount.textContent = this.event.listenerCount } this.eventData.textContent = this.event.data highlight.highlightBlock(this.eventData) } filter (searchText) { let matches = this.event.channel.toLowerCase().includes(searchText) matches = matches || this.event.data.toLowerCase().includes(searchText) matches ? this.show() : this.hide() } } module.exports = IpcEventView },{"./selectable-view":26,"highlight.js":33}],17:[function(require,module,exports){ 'use strict' const Eval = require('./eval') exports.listenForEvents = () => { return Eval.execute(() => { // Return if events are already being listened to to prevent duplicates // when reloading the extension if (window.__devtron.events != null) { window.__devtron.events = [] return } window.__devtron.events = [] const ipcRenderer = require('electron').ipcRenderer const ignoredEvents = { 'ATOM_BROWSER_DEREFERENCE': true, 'ELECTRON_BROWSER_DEREFERENCE': true } const trackEvent = (channel, args, sent, sync) => { if (window.__devtron.evaling) return if (ignoredEvents.hasOwnProperty(channel)) return let data try { data = JSON.stringify(args) } catch (error) { data = `Failed to serialize args to JSON: ${error.message || error}` } window.__devtron.events.push({ channel: channel, data: data, listenerCount: ipcRenderer.listenerCount(channel), sent: !!sent, sync: !!sync }) } const originalEmit = ipcRenderer.emit ipcRenderer.emit = function (channel, event) { const args = Array.prototype.slice.call(arguments, 2) trackEvent(channel, args) return originalEmit.apply(ipcRenderer, arguments) } const originalSend = ipcRenderer.send ipcRenderer.send = function (channel) { const args = Array.prototype.slice.call(arguments, 1) trackEvent(channel, args, true) return originalSend.apply(ipcRenderer, arguments) } const originalSendSync = ipcRenderer.sendSync ipcRenderer.sendSync = function (channel) { const args = Array.prototype.slice.call(arguments, 1) trackEvent(channel, args, true, true) const returnValue = originalSendSync.apply(ipcRenderer, arguments) trackEvent(channel, [returnValue], false, true) return returnValue } }) } exports.getEvents = () => { return Eval.execute(() => { const events = window.__devtron.events if (events) window.__devtron.events = [] return events }).then((events) => { if (events) return events // Start listening for events if array is missing meaning // the window was reloaded return exports.listenForEvents().then(() => []) }) } },{"./eval":9,"electron":undefined}],18:[function(require,module,exports){ 'use strict' const Eval = require('./eval') const ipc = require('./ipc-helpers') const IpcEventView = require('./ipc-event-view') const View = require('./view') class IpcView extends View { constructor () { super('ipc-view') this.children = [] this.recording = false this.handleEvents() } handleEvents () { this.debounceInput(this.searchBox, () => this.filterEvents()) this.clearButton.addEventListener('click', () => this.clear()) this.recordButton.addEventListener('click', () => this.toggleRecording()) this.docsButton.addEventListener('click', () => this.openDocs()) this.hideInternalButton.addEventListener('click', () => this.toggleHideInternal()) } toggleHideInternal () { if (this.hideInternal) { this.hideInternalButton.classList.remove('active') this.hideInternal = false this.children.forEach((child) => { if (child.internalEvent) child.show() }) } else { this.hideInternalButton.classList.add('active') this.hideInternal = true this.children.forEach((child) => { if (child.internalEvent) child.hide() }) } } toggleRecording () { if (this.recording) { this.stopRecording() this.recordButton.classList.remove('active') } else { this.startRecording() this.recordButton.classList.add('active') } } startRecording () { ipc.listenForEvents().then(() => { this.recording = true this.addNewEvents() }).catch((error) => { console.error('Listening for IPC events failed') console.error(error.stack || error) }) } stopRecording () { clearTimeout(this.timeoutId) this.recording = false } openDocs () { Eval.openExternal('http://electron.atom.io/docs/latest/api/ipc-main') } clear () { this.ipcTable.innerHTML = '' this.destroyChildren() } addNewEvents () { ipc.getEvents().then((events) => { if (!this.recording) return events.forEach((event) => this.addEvent(event)) this.timeoutId = setTimeout(() => this.addNewEvents(), 333) }).catch((error) => { console.error('Getting IPC events failed') console.error(error.stack || error) }) } addEvent (event) { this.tableDescription.classList.add('hidden') const eventView = new IpcEventView(event, this.ipcTable) this.children.push(eventView) this.filterIncomingEvent(eventView) } filterIncomingEvent (view) { if (this.hideInternal && view.internalEvent) { view.hide() } else { const searchText = this.getFilterText() if (searchText) view.filter(searchText) } } filterEvents () { const searchText = this.getFilterText() if (searchText) { this.children.forEach((child) => child.filter(searchText)) } else { this.children.forEach((child) => child.show()) } } getFilterText () { return this.searchBox.value.toLowerCase() } } module.exports = IpcView },{"./eval":9,"./ipc-event-view":16,"./ipc-helpers":17,"./view":28}],19:[function(require,module,exports){ (function (process){ 'use strict' const Eval = require('./eval') exports.isUsingAsar = () => { return Eval.execute(() => { const mainPath = require('electron').remote.process.mainModule.filename return /[\\/]app\.asar[\\/]/.test(mainPath) }) } exports.isListeningForCrashEvents = () => { return Eval.execute(() => { const webContents = require('electron').remote.getCurrentWebContents() // For versions less than 1.x.y // Electron has an crashed listener, so look for more than 1 const crashedForwarding = /^0/.test(process.versions.electron) const minCount = crashedForwarding ? 1 : 0 return webContents.listenerCount('crashed') > minCount }) } exports.isListeningForUnresponsiveEvents = () => { return Eval.execute(() => { const browserWindow = require('electron').remote.getCurrentWindow() return browserWindow.listenerCount('unresponsive') > 0 }) } exports.isListeningForUncaughtExceptionEvents = () => { return Eval.execute(() => { const mainProcess = require('electron').remote.process // Electron has an uncaughtException listener, so look for more than 1 return mainProcess.listenerCount('uncaughtException') > 1 }) } exports.getCurrentElectronVersion = () => { return Eval.execute(() => { return process.versions.electron }) } exports.getLatestElectronVersion = () => { return Eval.execute(() => { return window.__devtron.latestElectronVersion }) } exports.fetchLatestVersion = () => { return Eval.execute(() => { window.fetch('https://atom.io/download/atom-shell/index.json') .then((response) => { return response.json() }).then((versions) => { window.__devtron.latestElectronVersion = versions[0].version }).catch(() => { window.__devtron.latestElectronVersion = 'unknown' }) }) } }).call(this,require('_process')) },{"./eval":9,"_process":188,"electron":undefined}],20:[function(require,module,exports){ 'use strict' const highlight = require('highlight.js') const Lint = require('./lint-helpers') const View = require('./view') class LintView extends View { constructor () { super('lint-view') this.handleEvents() this.highlightBlocks() } reload () { this.lint() } highlightBlocks () { highlight.highlightBlock(this.crashedExample) highlight.highlightBlock(this.unresponsiveExample) highlight.highlightBlock(this.uncaughtExample) } handleEvents () { this.lintButton.addEventListener('click', () => this.lint()) } updateAlert (alertElement, descriptionElement, passing) { if (passing) { alertElement.classList.add('alert-lint-pass') descriptionElement.classList.add('hidden') } else { alertElement.classList.add('alert-lint-fail') descriptionElement.classList.remove('hidden') } alertElement.classList.remove('hidden') this.tableDescription.classList.add('hidden') } lint () { Lint.isUsingAsar().then((usingAsar) => { this.updateAlert(this.usingAsar, this.asarDescription, usingAsar) }) Lint.isListeningForCrashEvents().then((listening) => { this.updateAlert(this.crashListener, this.crashDescription, listening) }) Lint.isListeningForUnresponsiveEvents().then((listening) => { this.updateAlert(this.unresponsiveListener, this.unresponsiveDescription, listening) }) Lint.isListeningForUncaughtExceptionEvents().then((listening) => { this.updateAlert(this.uncaughtListener, this.uncaughtDescription, listening) }) this.checkVersion() } checkVersion () { Lint.getCurrentElectronVersion().then((version) => { this.currentVersion = version this.updateVersion() }) Lint.fetchLatestVersion() this.checkLatestVersion() } checkLatestVersion () { Lint.getLatestElectronVersion().then((version) => { if (version) { this.latestVersion = version this.updateVersion() } else { setTimeout(() => this.checkLatestVersion(), 250) } }) } updateVersion () { if (!this.latestVersion || !this.currentVersion) return const upToDate = this.latestVersion === this.currentVersion this.updateAlert(this.outdated, this.outdatedDescription, upToDate) this.latestLabel.textContent = this.latestVersion this.versionLabel.textContent = this.currentVersion } } module.exports = LintView },{"./lint-helpers":19,"./view":28,"highlight.js":33}],21:[function(require,module,exports){ 'use strict' const Eval = require('./eval') const Module = require('./module') const loadSizes = (mainModule) => { let totalSize = 0 return Promise.all(mainModule.toArray().map((module) => { return Eval.getFileSize(module.path).then((size) => { totalSize += size return module.setSize(size) }) })).then(() => { mainModule.totalSize = totalSize return mainModule }) } const loadVersions = (mainModule) => { return Promise.all(mainModule.toArray().map((module) => { return Eval.getFileVersion(module.path).then((version) => module.setVersion(version)) })).then(() => mainModule) } const createModules = (mainModule) => { const resourcesPath = mainModule.resourcesPath const appName = mainModule.appName const processModule = (node) => { const module = new Module(node.path, resourcesPath, appName) node.children.forEach((childNode) => { module.addChild(processModule(childNode)) }) return module } const convertedMainModule = processModule(mainModule) convertedMainModule.count = mainModule.count return convertedMainModule } const getRenderRequireGraph = () => { return Eval.execute(() => { let count = 0 const walkModule = (module) => { count++ let modulePath = module.filename || module.id if (process.platform === 'win32') { modulePath = modulePath.replace(/\\/g, '/') } return { path: modulePath, children: module.children.map(walkModule) } } const mainModule = walkModule(process.mainModule) mainModule.resourcesPath = process.resourcesPath mainModule.appName = require('electron').remote.app.getName() mainModule.count = count return mainModule }) } const getMainRequireGraph = () => { return Eval.execute(() => { let process = require('electron').remote.process let count = 0 const walkModule = (module) => { count++ let modulePath = module.filename || module.id if (process.platform === 'win32') { modulePath = modulePath.replace(/\\/g, '/') } return { path: modulePath, children: module.children.map(walkModule) } } const mainModule = walkModule(process.mainModule) mainModule.resourcesPath = process.resourcesPath mainModule.appName = require('electron').remote.app.getName() mainModule.count = count return mainModule }) } exports.getRenderModules = () => { return getRenderRequireGraph().then(createModules).then(loadSizes).then(loadVersions) } exports.getMainModules = () => { return getMainRequireGraph().then(createModules).then(loadSizes).then(loadVersions) } },{"./eval":9,"./module":23,"electron":undefined}],22:[function(require,module,exports){ 'use strict' const ExpandableView = require('./expandable-view') const Humanize = require('humanize-plus') class ModuleView extends ExpandableView { constructor (module, table, parent) { super('requires-table-row') this.parent = parent this.module = module this.table = table table.appendChild(this.element) this.render() this.children = this.module.children.map((child) => { return new ModuleView(child, table, this) }) this.module.getDepth() === 1 ? this.expand() : this.collapse() if (!this.module.hasChildren()) this.disclosure.style.display = 'none' this.handleEvents() } handleEvents () { this.listenForSelection(this.table) this.listenForSelectionKeys(this.table.parentElement) this.listenForExpanderKeys(this.table.parentElement) } getHumanizedSize () { const size = this.module.getSize() return Humanize.fileSize(size).replace('bytes', 'B') } render () { this.moduleName.textContent = this.module.getLibrary() this.moduleName.title = this.module.getLibrary() this.moduleVersion.textContent = this.module.getVersion() this.fileSize.textContent = this.getHumanizedSize() this.fileName.textContent = this.module.getName() this.fileName.title = this.module.path this.moduleDirectory.textContent = this.module.getDirectory() this.moduleDirectory.title = this.module.path this.pathSection.style['padding-left'] = `${(this.module.getDepth()) * 15}px` } filter (searchText) { this.collapse() let matches = this.module.getId().includes(searchText) matches = matches || this.module.getName().toLowerCase().includes(searchText) this.children.forEach((child) => { if (child.filter(searchText)) matches = true }) if (matches) { this.markCollapsed() this.show() this.markExpanded() } else { this.hide() } return matches } } module.exports = ModuleView },{"./expandable-view":14,"humanize-plus":186}],23:[function(require,module,exports){ 'use strict' class Module { constructor (path, resourcesPath, appName) { this.path = path this.resourcesPath = resourcesPath this.appName = appName this.size = -1 this.version = '' this.children = [] } setVersion (version) { this.version = version return this } getVersion () { return this.version } setSize (size) { this.size = size return this } getSize () { return this.size } hasChildren () { return this.children.length > 0 } addChild (child) { this.children.push(child) child.parent = this } getPath () { return this.path } getDepth () { let depth = 1 let parent = this.parent while (parent != null) { depth++ parent = parent.parent } return depth } getName () { if (!this.name) this.name = /\/([^\/]+)$/.exec(this.path)[1] return this.name } getDirectory () { let directoryPath = /(.+)\/[^\/]+$/.exec(this.path)[1] if (directoryPath.indexOf(this.resourcesPath) === 0) { directoryPath = directoryPath.substring(this.resourcesPath.length + 1) } return directoryPath } computeLibrary () { if (/\/atom\.asar\/(browser|common|renderer)\//.test(this.path)) return 'Electron' const libraryPattern = /\/node_modules\/([^\/]+)(?=\/)/g let match = libraryPattern.exec(this.path) while (match != null) { let library = match[1] match = libraryPattern.exec(this.path) if (match == null) return library } return this.appName } getLibrary () { if (!this.library) this.library = this.computeLibrary() return this.library } getId () { if (!this.id) this.id = this.getLibrary().toLowerCase() return this.id } visit (callback) { callback(this) this.children.forEach((child) => child.visit(callback)) } toArray () { const modules = [] this.visit((module) => modules.push(module)) return modules } } module.exports = Module },{}],24:[function(require,module,exports){ 'use strict' const Humanize = require('humanize-plus') const modules = require('./module-helpers') const ModuleView = require('./module-view') const View = require('./view') class ModulesView extends View { constructor () { super('modules-view') this.handleEvents() } reload () { this.loadGraph() } focus () { if (this.mainProcessTable.classList.contains('hidden')) { this.renderRequireRows.focus() } else { this.mainRequireRows.focus() } } handleEvents () { this.loadButton.addEventListener('click', () => this.loadGraph()) this.debounceInput(this.searchBox, () => this.filterGraph()) this.mainProcessTab.addEventListener('click', () => { this.mainProcessTab.classList.add('active') this.renderProcessTab.classList.remove('active') this.mainProcessTable.classList.remove('hidden') this.renderProcessTable.classList.add('hidden') this.mainRequireRows.focus() }) this.renderProcessTab.addEventListener('click', () => { this.mainProcessTab.classList.remove('active') this.renderProcessTab.classList.add('active') this.mainProcessTable.classList.add('hidden') this.renderProcessTable.classList.remove('hidden') this.renderRequireRows.focus() }) } getTabLabelSuffix (mainModule) { const count = mainModule.count.toLocaleString() const size = Humanize.fileSize(mainModule.totalSize) return `- ${count} files, ${size}` } loadGraph () { modules.getRenderModules().then((mainModule) => { this.tableDescription.classList.add('hidden') const suffix = this.getTabLabelSuffix(mainModule) this.renderProcessTab.textContent = `Renderer Process ${suffix}` this.renderRequireRows.innerHTML = '' if (this.rootRenderView) this.rootRenderView.destroy() this.rootRenderView = new ModuleView(mainModule, this.renderRequireRows) this.rootRenderView.select() }).catch((error) => { console.error('Loading render modules failed') console.error(error.stack || error) }) modules.getMainModules().then((mainModule) => { const suffix = this.getTabLabelSuffix(mainModule) this.mainProcessTab.textContent = `Main Process ${suffix}` this.mainRequireRows.innerHTML = '' if (this.rootMainView) this.rootMainView.destroy() this.rootMainView = new ModuleView(mainModule, this.mainRequireRows) this.rootMainView.select() }).catch((error) => { console.error('Loading main modules failed') console.error(error.stack || error) }) } filterGraph () { const searchText = this.searchBox.value.toLowerCase() if (searchText) { this.rootRenderView.filter(searchText) this.rootMainView.filter(searchText) } else { this.rootRenderView.collapseAll() this.rootRenderView.expand() this.rootMainView.collapseAll() this.rootMainView.expand() } } } module.exports = ModulesView },{"./module-helpers":21,"./module-view":22,"./view":28,"humanize-plus":186}],25:[function(require,module,exports){ 'use strict' const highlight = require('highlight.js') const View = require('./view') class NodeIntegrationView extends View { constructor () { super('node-integration-view') this.highlightBlocks() } highlightBlocks () { highlight.highlightBlock(this.browserWindowExample) highlight.highlightBlock(this.devtronExample) highlight.highlightBlock(this.envCheckExample) } } module.exports = NodeIntegrationView },{"./view":28,"highlight.js":33}],26:[function(require,module,exports){ 'use strict' const View = require('./view') class SelectableView extends View { select () { this.selected = true this.element.classList.add('active') this.element.scrollIntoViewIfNeeded() } deselect () { this.selected = false this.element.classList.remove('active') } selectNext () { let next = this.element.nextElementSibling while (next && (next.view instanceof SelectableView)) { if (next.view.isHidden()) { next = next.nextElementSibling continue } this.deselect() next.view.select() break } } selectPrevious () { let previous = this.element.previousElementSibling while (previous && (previous.view instanceof SelectableView)) { if (previous.view.isHidden()) { previous = previous.previousElementSibling continue } this.deselect() previous.view.select() break } } listenForSelection (emitter) { this.bindListener(emitter, 'mousedown', (event) => { if (this.element.contains(event.target)) { this.select() } else { this.deselect() } }) } listenForSelectionKeys (emitter) { this.bindListener(emitter, 'keydown', (event) => { if (!this.selected) return if (event.altKey || event.metaKey || event.ctrlKey) return switch (event.code) { case 'ArrowDown': this.selectNext() event.stopImmediatePropagation() event.preventDefault() break case 'ArrowUp': this.selectPrevious() event.stopImmediatePropagation() event.preventDefault() break } }) } } module.exports = SelectableView },{"./view":28}],27:[function(require,module,exports){ 'use strict' const View = require('./view') class SidebarView extends View { constructor () { super('sidebar-view') this.panes = [] this.links = [this.requireLink, this.eventsLink, this.ipcLink, this.lintLink, this.accessibilityLink, this.aboutLink] this.panesElement = document.querySelector('#pane-group') this.panesElement.appendChild(this.element) this.handleEvents() } handleEvents () { document.body.addEventListener('keydown', (event) => { if (event.ctrlKey || event.metaKey) return if (!event.altKey) return switch (event.code) { case 'ArrowDown': this.selectNext() event.stopImmediatePropagation() event.preventDefault() break case 'ArrowUp': this.selectPrevious() event.stopImmediatePropagation() event.preventDefault() break } }) document.body.addEventListener('keydown', (event) => { if ((event.ctrlKey || event.metaKey) && event.code === 'KeyE') { this.activePane.reload() this.activePane.focus() event.stopImmediatePropagation() event.preventDefault() } }) this.element.addEventListener('mousedown', (event) => { let paneLink = event.target.dataset.paneLink if (paneLink) this.selectPane(paneLink) }) } activateLink (name) { this.links.forEach((link) => { if (link.dataset.paneLink === name) { link.classList.add('active') } else { link.classList.remove('active') } }) } addPane (view) { if (this.panes.length === 0) this.activePane = view this.panes.push(view) this.panesElement.appendChild(view.element) } findPane (name) { return this.panes.find((view) => view.element.dataset.pane === name) } selectPane (name) { const pane = this.findPane(name) if (!pane) return this.panes.forEach((view) => view.hide()) pane.show() pane.focus() this.activePane = pane this.activateLink(name) } selectPrevious () { const selectedIndex = this.panes.indexOf(this.activePane) const previousIndex = Math.max(selectedIndex - 1, 0) this.selectPane(this.panes[previousIndex].element.dataset.pane) } selectNext () { const selectedIndex = this.panes.indexOf(this.activePane) const nextIndex = Math.min(selectedIndex + 1, this.panes.length - 1) this.selectPane(this.panes[nextIndex].element.dataset.pane) } } module.exports = SidebarView },{"./view":28}],28:[function(require,module,exports){ 'use strict' class View { static queryForEach (element, selector, callback) { const elements = element.querySelectorAll(selector) Array.prototype.forEach.call(elements, callback) } constructor (viewId) { this.id = viewId this.listeners = [] this.element = this.createElement() this.element.v