// dev tool: scan for calls to members/functions that are never defined 'use strict'; const fs = require('fs'); const files = fs.readdirSync('js').filter(f => f.endsWith('.js')); let all = ''; for (const f of files) all += fs.readFileSync('js/' + f, 'utf8') + '\n'; const defs = new Set(); for (const m of all.matchAll(/(?:^|\n)[ \t]*(?:async\s+)?([A-Za-z_$][\w$]*)\s*\([^()]*\)\s*\{/g)) defs.add(m[1]); for (const m of all.matchAll(/\bfunction\s+([A-Za-z_$][\w$]*)/g)) defs.add(m[1]); for (const m of all.matchAll(/(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g)) defs.add(m[1]); const allow = new Set([ 'push','pop','shift','unshift','splice','slice','concat','join','map','filter','forEach', 'reduce','find','findIndex','some','every','includes','indexOf','sort','fill','keys', 'values','entries','from','isArray','assign','create','freeze','getOwnPropertyNames', 'setItem','getItem','removeItem','clear','log','warn','error','now','floor','ceil','round', 'abs','min','max','sqrt','pow','sin','cos','tan','atan','atan2','random','hypot','sign','exp', 'add','has','delete','get','set', 'translate','rotate','scale','save','restore','beginPath','closePath','moveTo','lineTo','arc', 'arcTo','quadraticCurveTo','bezierCurveTo','rect','fill','stroke','clip','fillText','strokeText', 'measureText','drawImage','createLinearGradient','createRadialGradient','createPattern','fillRect', 'strokeRect','clearRect','setTransform','resetTransform','ellipse', 'play','pause','resume','connect','start','stop','setValueAtTime','linearRampToValueAtTime', 'exponentialRampToValueAtTime','createGain','createOscillator','createBuffer','createBufferSource', 'createBiquadFilter', 'addEventListener','removeEventListener','preventDefault','stopPropagation','appendChild', 'removeChild','remove','querySelector','querySelectorAll','getElementById','createElement', 'toggle','contains','setAttribute','getAttribute','insertBefore','closest','matches', 'test','exec','replace','split','trim','toLowerCase','toUpperCase','charAt','charCodeAt', 'padStart','padEnd','repeat','match','matchAll','toString','toFixed','stringify','parse', 'requestAnimationFrame','setTimeout','setInterval','clearTimeout','clearInterval','fetch', 'then','catch','finally','bind','call','apply' ]); let bad = 0; for (const m of all.matchAll(/\b(UI|Sys|R|Snd|Store|GAME)\.([A-Za-z_$][\w$]*)\s*\(/g)) { const meth = m[2]; if (!allow.has(meth) && !defs.has(meth)) { console.log('MISSING:', m[1] + '.' + meth); bad++; } } console.log(bad === 0 ? 'No missing member calls OK' : bad + ' issues'); for (const f of ['render.js', 'systems.js', 'ui.js', 'audio.js', 'save.js']) { const src = fs.readFileSync('js/' + f, 'utf8'); for (const m of src.matchAll(/this\.(_?[A-Za-z_$][\w$]*)\s*\(/g)) { if (!defs.has(m[1])) console.log('MISSING this.' + m[1] + ' in ' + f); } } console.log('this-method scan done');