Files
hermes-config/skills/software-development/web-ui-repair/references/inline-script-bridging-checklist.md
T
2026-07-12 10:17:17 -04:00

3.7 KiB

Inline Script Bridging Checklist

When adding a plain <script> (non-module) to a page that uses ES modules, the inline script has NO access to module-level imports. Three categories of globals are commonly missing:

1. closeModal / Modal Helpers

Symptom: onclick="closeModal('...')" on buttons does nothing, or JS code calls closeModal() and gets ReferenceError.

Diagnosis:

grep -c 'window.closeModal' page.html
# 0 = missing

Fix: Add at the top of the inline script block:

window.closeModal = function(id) {
    var m = document.getElementById(id);
    if (m) { m.classList.add('hidden'); m.setAttribute('aria-hidden', 'true'); }
};

(Note: shared/modal-manager.js exports closeModal as a named ES module export — it does NOT set window.closeModal. Don't assume it's globally available.)

2. escapeHtml / Sanitization Functions

Symptom: escapeHtml is not defined when rendering user data in dynamically created HTML.

Diagnosis: shared/sanitize.js exports escapeHtml as a named ES module export only. It is NOT on window.

Fix: Add a local polyfill at the top of the inline script:

function escapeHtml(text) {
    var d = document.createElement('div');
    d.appendChild(document.createTextNode(text));
    return d.innerHTML;
}

3. Module-Level Data Functions

Symptom: Inline script needs to call a function defined in a module (e.g., batchCreateAppointments, loadAppointments) but can't import it.

Fix (exporter side — in the module):

// In appointments.js or similar module file
window.batchCreateAppointments = async function(data) {
    // Uses module-scoped `addDoc`, `collection`, `db`, `currentUser` etc.
    // ...
};

Fix (consumer side — in the inline script):

// Call the window-exposed function
const result = await window.batchCreateAppointments(selectedAppointments);

Place the window.* assignment in the module file, AFTER the function's dependencies are initialized (after onAuthStateChanged has fired, after imports are resolved).

4. Timing: Module vs Inline Execution Order

Symptom: window.someFunction is not a function when the inline script calls it at page load.

Root cause: Classic <script> tags run synchronously before deferred <script type="module"> tags. The module may not have executed yet.

Safe pattern: Only call window.* functions in event handlers (click, submit, etc.), not at script load time. By the time the user interacts, the module has loaded.

If you MUST call at load time, use a polling guard:

function waitFor(fn, timeout) {
    return new Promise((resolve, reject) => {
        const start = Date.now();
        const check = () => {
            if (typeof fn === 'function') return resolve();
            if (Date.now() - start > timeout) return reject(new Error('Timeout waiting for function'));
            setTimeout(check, 50);
        };
        check();
    });
}

5. Nginx Proxy for CORS-Sensitive APIs

Symptom: fetch() to an external API fails with CORS errors in the browser console.

Fix: Proxy the external API through nginx on the same origin:

location /deepseek/ {
    proxy_pass https://api.deepseek.com/;
    proxy_http_version 1.1;
    proxy_set_header Host api.deepseek.com;
    proxy_set_header Authorization "Bearer YOUR_API_KEY";
}

Then call /deepseek/chat/completions (relative URL) from the inline script — no CORS issue.

Verify: The proxy location must be in the same server { } block that serves the page. If the page is served on port 3447 but the proxy is on port 80, the relative URL won't reach it.