Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log warnings to the console #84

Merged
merged 6 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions public/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,23 @@ <h1 class="title">Hotwire Dev Tools</h1>
<fieldset>
<legend>Console Log</legend>
<form>
<label class="toggle">
<input class="toggle-checkbox" type="checkbox" id="log-warnings" />
<div class="toggle-switch"></div>
<span class="toggle-label">Log Warnings</span>
</label>

<label class="toggle">
<input class="toggle-checkbox" type="checkbox" id="monitor-events" />
<div class="toggle-switch"></div>
<span class="toggle-label">Monitor Events</span>
</label>
<div class="detail-panel-options-control monitor-events-toggle-element">
<button id="monitor-events-select-all">Select all</button>
<div class="monitor-events-collapse">
<div class="detail-panel-options-control monitor-events-toggle-element">
<button id="monitor-events-select-all">Select all</button>
</div>
<div class="detail-panel-options-wrapper monitor-events-toggle-element monitor-events-checkbox-container"></div>
</div>
<div class="detail-panel-options-wrapper monitor-events-toggle-element monitor-events-checkbox-container"></div>
</form>
</fieldset>
</body>
Expand Down
5 changes: 5 additions & 0 deletions public/styles/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ label,
user-select: none;
}

form {
display: flex;
flex-direction: column;
}

fieldset {
margin: 1rem 0;
border: 1px solid #ccc;
Expand Down
14 changes: 12 additions & 2 deletions src/content.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { debounce } from "./utils/utils"
import { turboStreamTargetElements } from "./utils/turbo_utils"
import { addHighlightOverlayToElements, removeHighlightOverlay } from "./utils/highlight"
import { MONITORING_EVENTS } from "./lib/monitoring_events"

import Devtool from "./lib/devtool"
import DetailPanel from "./components/detail_panel"
import DOMScanner from "./utils/dom_scanner"
import DiagnosticsChecker from "./lib/diagnostics_checker"

const LOCATION_ORIGIN = window.location.origin
const devTool = new Devtool(LOCATION_ORIGIN)
const detailPanel = new DetailPanel(devTool)
const diagnosticsChecker = new DiagnosticsChecker(devTool)

const highlightTurboFrames = () => {
const badgeClass = "hotwire-dev-tools-turbo-frame-info-badge"
Expand Down Expand Up @@ -137,7 +140,7 @@ const highlightStimulusControllers = () => {

const injectCustomScript = () => {
const existingScript = document.getElementById("hotwire-dev-tools-inject-script")
if (existingScript || !devTool.shouldRenderDetailPanel()) return
if (existingScript) return

const script = document.createElement("script")
script.src = chrome.runtime.getURL("dist/hotwire_dev_tools_inject_script.js")
Expand Down Expand Up @@ -165,6 +168,12 @@ const consoleLogTurboStream = (event) => {
console.log(message, turboStream)
}

const checkForWarnings = debounce(() => {
if (devTool.options.logWarnings) {
diagnosticsChecker.checkForWarnings()
}
}, 150)

const handleTurboFrameBadgeClick = (event) => {
navigator.clipboard.writeText(event.target.dataset.turboId).then(() => {
event.target.classList.add("copied")
Expand All @@ -189,6 +198,7 @@ const handleWindowMessage = (event) => {
if (event.data.registeredControllers) {
devTool.registeredStimulusControllers = event.data.registeredControllers
renderDetailPanel()
checkForWarnings()
}
break
case "turboDetails":
Expand Down Expand Up @@ -257,7 +267,7 @@ const init = async () => {
highlightTurboFrames()
highlightStimulusControllers()
renderDetailPanel()
console.log(DOMScanner.uniqueStimulusControllerIdentifiers)
checkForWarnings()
}

const events = ["turbolinks:load", "turbo:load", "turbo:frame-load", "hotwire-dev-tools:options-changed"]
Expand Down
1 change: 1 addition & 0 deletions src/lib/devtool.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export default class Devtool {
monitor: {
events: [],
},
logWarnings: true,
}
}
}
78 changes: 78 additions & 0 deletions src/lib/diagnostics_checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import DOMScanner from "../utils/dom_scanner"

export default class DiagnosticsChecker {
constructor(devTool) {
this.devTool = devTool
this.printedWarnings = []
this.logger = console
}

printWarning = (message, once = true, ...extraArgs) => {
if (once && this.printedWarnings.includes(message)) return

this.logger.warn(`Hotwire Dev Tools: ${message}`, ...extraArgs)
this.printedWarnings.push(message)
}

checkForWarnings = () => {
this._checkForDuplicatedTurboFrames()
this._checkForNonRegisteredStimulusControllers()
this._checkTurboPermanentElements()
this._checkStimulusTargetsNesting()
}

_checkForDuplicatedTurboFrames = () => {
const turboFramesIds = DOMScanner.turboFrameIds
const duplicatedIds = turboFramesIds.filter((id, index) => turboFramesIds.indexOf(id) !== index)

duplicatedIds.forEach((id) => {
this.printWarning(`Multiple Turbo Frames with the same ID '${id}' detected. This can cause unexpected behavior. Ensure that each Turbo Frame has a unique ID.`)
})
}

_checkForNonRegisteredStimulusControllers = () => {
const registeredStimulusControllers = this.devTool.registeredStimulusControllers
if (registeredStimulusControllers.length === 0) return

DOMScanner.uniqueStimulusControllerIdentifiers.forEach((controllerId) => {
const controllerRegistered = registeredStimulusControllers.includes(controllerId)

if (!controllerRegistered) {
this.printWarning(`The Stimulus controller '${controllerId}' does not appear to be registered. Learn more about registering Stimulus controllers here: https://stimulus.hotwired.dev/handbook/installing.`)
}
})
}

_checkStimulusTargetsNesting = () => {
DOMScanner.uniqueStimulusControllerIdentifiers.forEach((controllerId) => {
const dataSelector = `data-${controllerId}-target`
const targetElements = document.querySelectorAll(`[${dataSelector}`)
targetElements.forEach((element) => {
const parent = element.closest(`[data-controller="${controllerId}"]`)
if (!parent) {
const targetName = element.getAttribute(`${dataSelector}`)
this.printWarning(`The Stimulus target '${targetName}' is not inside the Stimulus controller '${controllerId}'`)
}
})
})
}

_checkTurboPermanentElements = () => {
const turboPermanentElements = DOMScanner.turboPermanentElements
if (turboPermanentElements.length === 0) return

turboPermanentElements.forEach((element) => {
const id = element.id
if (id === "") {
const message = `Hotwire Dev Tools: Turbo Permanent Element detected without an ID. Turbo Permanent Elements must have a unique ID to work correctly.`
this.printWarning(message, true, element)
}

const idIsDuplicated = id && document.querySelectorAll(`#${id}`).length > 1
if (idIsDuplicated) {
const message = `Hotwire Dev Tools: Turbo Permanent Element with ID '${id}' doesn't have a unique ID. Turbo Permanent Elements must have a unique ID to work correctly.`
this.printWarning(message, true, element)
}
})
}
}
9 changes: 9 additions & 0 deletions src/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const monitorEventsToggles = document.querySelectorAll(".monitor-events-toggle-e
const monitorEventsCheckboxContainer = document.querySelector(".monitor-events-checkbox-container")
const monitorEventsSelectAll = document.getElementById("monitor-events-select-all")

const logWarning = document.getElementById("log-warnings")

const toggleInputs = (toggleElements, show) => {
toggleElements.forEach((element) => {
element.classList.toggle("d-none", !show)
Expand Down Expand Up @@ -81,6 +83,8 @@ const initializeForm = async (options) => {

monitorEvents.checked = options.monitor.events.length > 0

logWarning.checked = options.logWarnings

if (devTool.isFirefox) {
// In Firefox the color picker inside an extension popup doesn't really work (See https://github.com/leonvogt/hotwire-dev-tools/issues/20)
// Workaround: Change the input type to text so the user can input the color manually
Expand Down Expand Up @@ -285,6 +289,11 @@ const setupEventListeners = (options) => {
options.monitor.events = allChecked ? [] : MONITORING_EVENTS
saveOptions(options)
})

logWarning.addEventListener("change", (event) => {
options.logWarnings = event.target.checked
saveOptions(options)
})
}

const getCurrentTabOrigin = async () => {
Expand Down
8 changes: 8 additions & 0 deletions src/utils/dom_scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ export default class DOMScanner {
return document.querySelectorAll("turbo-frame")
}

static get turboFrameIds() {
return Array.from(this.turboFrameElements).map((turboFrame) => turboFrame.id)
}

static get turboPermanentElements() {
return document.querySelectorAll("[data-turbo-permanent]")
}

// Stimulus
static get stimulusControllerElements() {
return document.querySelectorAll("[data-controller]")
Expand Down
Loading