Files
hermes-config/skills/self-hosting/shop-pro-quote/references/js-debugging.md
T
2026-07-12 10:17:17 -04:00

88 lines
3.8 KiB
Markdown

# ShopProQuote JS Debugging Patterns
## Silent module load failure from SyntaxError
**Symptom:** A feature is "stuck loading" despite the HTML being correct and the backend responding. The page shows a permanent "Loading..." state.
**Root cause:** A syntax error in a `<script>`-loaded JS file prevents the ENTIRE module from executing. Common in ShopProQuote because JS files are loaded as regular scripts (not ES modules with isolated scopes).
**Example from dashboard briefing (June 2026):**
```js
// Line ~3926
const briefingText = dsResult.choices?.[0]?.message?.content?.trim() || '';
// ... later, line ~3944, same function scope:
const briefingText = contentDiv.textContent || ''; // 💥 SyntaxError: redeclaration of const
```
JavaScript refuses to parse the file, `window.generateDailyBriefing` is never defined, the inline fallback in `index.html` polls forever for it, and the user sees "Loading briefing..." permanently.
## Debugging steps
1. **Check if the function exists at all:**
```js
// In browser console
typeof window.generateDailyBriefing // 'undefined' = module never loaded
```
2. **Syntax-check the file (if Node.js available):**
```bash
node -c dashboard.js
```
3. **Without Node.js, grep for duplicate declarations:**
```bash
# Find duplicate const declarations in the same function
sed -n '/async function generateDailyBriefing/,/^ }/p' dashboard.js | grep -P "^\s*const \w+ =" | awk '{print $2}' | sort | uniq -d
```
4. **Check browser console** — a SyntaxError appears but users often miss it.
## Proxy verification pattern
When an AI feature relies on an nginx proxy, verify the proxy works independently of the frontend:
```bash
curl -k -X POST https://127.0.0.1/deepseek/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"test"}],"max_tokens":10}'
```
This separates "proxy broken" from "JS broken."
## "Button does nothing" — element existence guard pattern
When clicking a submit button produces literally zero visible effect (no spinner, no error, no console message beyond a hidden TypeError), the most likely cause is that one or more DOM elements the handler reads from are `null`. The form element exists in the HTML, but at the time the handler fires, it's not in the DOM (dynamic insertion timing, modal re-opening, or form.reset() clearing dynamic content).
### Defensive pattern
Instead of directly chaining `.value` on `document.getElementById()` calls, capture each element reference first, check for null, and log which ones are missing:
```js
async function handleCreateTask() {
console.log('🔍 handleCreateTask() called');
const titleEl = document.getElementById('new-task-title');
const dueDateEl = document.getElementById('new-task-due-date');
const dueTimeEl = document.getElementById('new-task-due-time');
if (!titleEl || !dueDateEl || !dueTimeEl) {
console.error('❌ Missing form elements:', {
title: !!titleEl, dueDate: !!dueDateEl, dueTime: !!dueTimeEl
});
showNotification('Form elements not found. Please refresh the page.', true);
return;
}
const title = titleEl.value.trim();
// ... rest of handler
}
```
This pattern accomplishes three things simultaneously:
1. The `console.log` at line 1 proves the handler even fired (vs HTML5 validation blocking submit)
2. The null checks pinpoint exactly which element is missing
3. The early return with notification gives the user a visible message instead of silent failure
### Why this beats try/catch alone
A `try/catch` wrapping the handler body catches the TypeError, but you don't know WHICH element caused it without the logged boolean map. And you can't show a meaningful error message — just "something went wrong."