mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-03 23:21:19 +00:00
feat(ci): frontend eslint no-undef gate — catches renamed-function-caller class of bugs (fixes #1342) (#1344)
**TDD:** red commit `03ea965` (canary undef var → CI fails) → green commit `b514aeb` (canary removed → CI passes). CI URL appears in the Checks tab once GitHub Actions queues this branch. `Fixes #1342` ## What ships - **`.eslintrc.json`** at repo root — eslint 8 legacy-config format. `no-undef: error`, `no-unused-vars: warn` (with `^_` allowlist). - **CI step** in `.github/workflows/deploy.yml` (job `go-test`, after JS unit tests, before proto + Playwright): `npm install --no-save eslint@8 && npx eslint public/*.js`. `--no-save` keeps `node_modules` and `package-lock.json` out of the tree (already gitignored). - **One pre-existing fix** in `public/map.js`: `typeof esc === 'function'` → `typeof globalThis.esc === 'function'`. `esc` is a *local* IIFE var in 5 other files, never exported as a true global; the optional lookup was structurally invalid under `no-undef`. Behavior unchanged. ## How this would have caught #1318 / PR #923 PR #923 renamed `drawAnimatedLine`, updated one caller in `public/live.js`, missed the other — leaving a reference to the undefined `hash` var. Playwright didn't hit that path. Reverting #1325 locally (re-introducing the bug) → eslint flags `hash` as `no-undef` → red. With the gate in place, #923 never lands. ## The "quiet pile of globals" reality The config declares **257 globals**. They were discovered by walking `public/*.js` for two patterns: 1. `window.X = ...` assignments (the explicit exports — 168 of them) 2. Top-level `function`/`const`/`let`/`var` declarations in non-IIFE files (the implicit exports — Go-style cross-file linking via shared HTML `<script>` order) Plus 9 vendor/runtime names (`L`, `Chart`, `QRCode`, `qrcode`, `module`, `global`, `process`, `require`, `exports`, `__filename`, `__dirname`) for dual-runtime files like `url-state.js`, `packet-filter.js`, `hash-color.js`, `filter-ux.js` that are also `require()`-d by Node tests. This is honest documentation of an architectural reality, not a workaround. Future refactor → modules will collapse this list. ## Latent bugs discovered **Zero `no-undef` errors against the current `public/*.js` tree** after globals were enumerated honestly. The would-be-#1318-class bug count today: 0. The gate's job is forward-looking — block the next one. ## Out of scope (acknowledged from acceptance criteria) - Inline `<script>` blocks in `public/*.html` — separate ticket. - Per-PR delta-coverage gate — separate ticket. - pr-preflight grep for arg-count mismatch — separate ticket. ## Preflight `bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh origin/master` → exit 0, clean. --------- Co-authored-by: openclaw-bot <bot@openclaw.local>
This commit is contained in:
+279
@@ -0,0 +1,279 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
"sourceType": "script"
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2022": true
|
||||
},
|
||||
"globals": {
|
||||
"AreaFilter": "readonly",
|
||||
"CACHE_INVALIDATE_MS": "readonly",
|
||||
"CLIENT_CONFIG": "readonly",
|
||||
"CLIENT_TTL": "readonly",
|
||||
"ChannelColorPicker": "readonly",
|
||||
"ChannelColors": "readonly",
|
||||
"ChannelDecrypt": "readonly",
|
||||
"ChannelQR": "readonly",
|
||||
"Chart": "readonly",
|
||||
"DIST_THRESHOLDS": "readonly",
|
||||
"DragManager": "readonly",
|
||||
"EXTERNAL_URLS": "readonly",
|
||||
"FAV_KEY": "readonly",
|
||||
"FilterUX": "readonly",
|
||||
"GestureHints": "readonly",
|
||||
"HEALTH_THRESHOLDS": "readonly",
|
||||
"HashColor": "readonly",
|
||||
"HopDisplay": "readonly",
|
||||
"HopResolver": "readonly",
|
||||
"IATA_CITIES": "readonly",
|
||||
"IATA_COORDS_GEO": "readonly",
|
||||
"L": "readonly",
|
||||
"LIMITS": "readonly",
|
||||
"Logo": "readonly",
|
||||
"MAX_HOP_DIST": "readonly",
|
||||
"MeshAudio": "readonly",
|
||||
"MeshConfigReady": "readonly",
|
||||
"PAYLOAD_COLORS": "readonly",
|
||||
"PAYLOAD_TYPES": "readonly",
|
||||
"PERF_SLOW_MS": "readonly",
|
||||
"PROPAGATION_BUFFER_MS": "readonly",
|
||||
"PULL_THRESHOLD_PX": "readonly",
|
||||
"PacketFilter": "readonly",
|
||||
"PathInspector": "readonly",
|
||||
"QRCode": "readonly",
|
||||
"ROLE_COLORS": "readonly",
|
||||
"ROLE_EMOJI": "readonly",
|
||||
"ROLE_LABELS": "readonly",
|
||||
"ROLE_SHAPES": "readonly",
|
||||
"ROLE_SORT": "readonly",
|
||||
"ROLE_STYLE": "readonly",
|
||||
"ROUTE_TYPES": "readonly",
|
||||
"RegionFilter": "readonly",
|
||||
"SITE_CONFIG": "readonly",
|
||||
"SKEW_SEVERITY_COLORS": "readonly",
|
||||
"SKEW_SEVERITY_LABELS": "readonly",
|
||||
"SKEW_SEVERITY_ORDER": "readonly",
|
||||
"SNR_THRESHOLDS": "readonly",
|
||||
"SlideOver": "readonly",
|
||||
"TILE_DARK": "readonly",
|
||||
"TILE_LIGHT": "readonly",
|
||||
"TYPE_COLORS": "readonly",
|
||||
"TableResponsive": "readonly",
|
||||
"TableSort": "readonly",
|
||||
"TouchGestures": "readonly",
|
||||
"TracesHelpers": "readonly",
|
||||
"URLState": "readonly",
|
||||
"WS_RECONNECT_MS": "readonly",
|
||||
"_SITE_CONFIG_ORIGINAL_HOME": "readonly",
|
||||
"__PERF_LOG_RENDER": "readonly",
|
||||
"__bottomNavInitDone": "readonly",
|
||||
"__corescopeLogo": "readonly",
|
||||
"__dirname": "readonly",
|
||||
"__filename": "readonly",
|
||||
"__gestureHints1065Init": "readonly",
|
||||
"__liveMQLBindCount": "readonly",
|
||||
"__meshcoreMapInternals": "readonly",
|
||||
"__navDrawer": "readonly",
|
||||
"__navDrawerPointerBindCount": "readonly",
|
||||
"__pathOverflowWired": "readonly",
|
||||
"__scrollLock": "readonly",
|
||||
"__touchGestures1062InitCount": "readonly",
|
||||
"_analyticsChannelTbodyHtml": "readonly",
|
||||
"_analyticsChannelTheadHtml": "readonly",
|
||||
"_analyticsDecorateChannels": "readonly",
|
||||
"_analyticsHashStatCardsHtml": "readonly",
|
||||
"_analyticsLoadChannelSort": "readonly",
|
||||
"_analyticsRenderCollisionsFromServer": "readonly",
|
||||
"_analyticsRenderMultiByteAdopters": "readonly",
|
||||
"_analyticsRenderMultiByteCapability": "readonly",
|
||||
"_analyticsRfNFColumnChart": "readonly",
|
||||
"_analyticsSaveChannelSort": "readonly",
|
||||
"_analyticsSortChannels": "readonly",
|
||||
"_apiCache": "readonly",
|
||||
"_apiPerf": "readonly",
|
||||
"_channelsBeginMessageRequestForTest": "readonly",
|
||||
"_channelsGetStateForTest": "readonly",
|
||||
"_channelsHandleWSBatchForTest": "readonly",
|
||||
"_channelsIsStaleMessageRequestForTest": "readonly",
|
||||
"_channelsLoadChannelsForTest": "readonly",
|
||||
"_channelsProcessWSBatchForTest": "readonly",
|
||||
"_channelsReconcileSelectionForTest": "readonly",
|
||||
"_channelsRefreshMessagesForTest": "readonly",
|
||||
"_channelsSelectChannelForTest": "readonly",
|
||||
"_channelsSetObserverRegionsForTest": "readonly",
|
||||
"_channelsSetStateForTest": "readonly",
|
||||
"_channelsShouldProcessWSMessageForRegion": "readonly",
|
||||
"_customizerV2": "readonly",
|
||||
"_ensurePullIndicator": "readonly",
|
||||
"_inflight": "readonly",
|
||||
"_isTouchDevice": "readonly",
|
||||
"_liveAddFeedItem": "readonly",
|
||||
"_liveBufferPacket": "readonly",
|
||||
"_liveBuildClickablePathPopupHtml": "readonly",
|
||||
"_liveBuildObserverIataMap": "readonly",
|
||||
"_liveClickablePaths": "readonly",
|
||||
"_liveDbPacketToLive": "readonly",
|
||||
"_liveExpandToBufferEntries": "readonly",
|
||||
"_liveExpandToBufferEntriesAsync": "readonly",
|
||||
"_liveFormatLiveTimestampHtml": "readonly",
|
||||
"_liveGetFavoritePubkeys": "readonly",
|
||||
"_liveGetNodeFilterKeys": "readonly",
|
||||
"_liveGetObserverIataMap": "readonly",
|
||||
"_liveIsNodeFavorited": "readonly",
|
||||
"_liveNodeActivity": "readonly",
|
||||
"_liveNodeData": "readonly",
|
||||
"_liveNodeMarkers": "readonly",
|
||||
"_livePacketInvolvesFavorite": "readonly",
|
||||
"_livePacketInvolvesFilterNode": "readonly",
|
||||
"_livePacketMatchesRegion": "readonly",
|
||||
"_livePruneClickablePaths": "readonly",
|
||||
"_livePruneStaleNodes": "readonly",
|
||||
"_liveRebuildFeedList": "readonly",
|
||||
"_liveResolveHopPositions": "readonly",
|
||||
"_liveSEG_MAP": "readonly",
|
||||
"_liveSetMarkerColor": "readonly",
|
||||
"_liveSetMarkerSize": "readonly",
|
||||
"_liveSetNodeFilter": "readonly",
|
||||
"_liveSetObserverIataMap": "readonly",
|
||||
"_liveSpeedLabel": "readonly",
|
||||
"_liveVCR": "readonly",
|
||||
"_liveVcrPause": "readonly",
|
||||
"_liveVcrResumeLive": "readonly",
|
||||
"_liveVcrSetMode": "readonly",
|
||||
"_liveVcrSpeedCycle": "readonly",
|
||||
"_live_packetTimestamp": "readonly",
|
||||
"_mapGetNeighborPubkeys": "readonly",
|
||||
"_mapSelectRefNode": "readonly",
|
||||
"_meshAudioVoices": "readonly",
|
||||
"_meshcoreHeatLayer": "readonly",
|
||||
"_meshcoreLiveHeatLayer": "readonly",
|
||||
"_nodesGetAllNodes": "readonly",
|
||||
"_nodesGetSortState": "readonly",
|
||||
"_nodesGetStatusInfo": "readonly",
|
||||
"_nodesGetStatusTooltip": "readonly",
|
||||
"_nodesIsAdvertMessage": "readonly",
|
||||
"_nodesMatchesSearch": "readonly",
|
||||
"_nodesRenderNodeTimestampHtml": "readonly",
|
||||
"_nodesRenderNodeTimestampText": "readonly",
|
||||
"_nodesSetAllNodes": "readonly",
|
||||
"_nodesSetSortState": "readonly",
|
||||
"_nodesSortArrow": "readonly",
|
||||
"_nodesSortNodes": "readonly",
|
||||
"_nodesSyncClaimedToFavorites": "readonly",
|
||||
"_nodesToggleSort": "readonly",
|
||||
"_packetsTestAPI": "readonly",
|
||||
"_panelCorner": "readonly",
|
||||
"_pendingPathInspectorRoute": "readonly",
|
||||
"_perfWriteSourcesPrev": "readonly",
|
||||
"_pullIndicator": "readonly",
|
||||
"_pullToast": "readonly",
|
||||
"_pullToastTimer": "readonly",
|
||||
"_reducedMotionMQL": "readonly",
|
||||
"_showPullToast": "readonly",
|
||||
"_themeRefreshTimer": "readonly",
|
||||
"_vcrFormatTime": "readonly",
|
||||
"addEventListener": "readonly",
|
||||
"api": "readonly",
|
||||
"apiPerf": "readonly",
|
||||
"bindFavStars": "readonly",
|
||||
"buildHexLegend": "readonly",
|
||||
"buildNodesQuery": "readonly",
|
||||
"buildPacketsQuery": "readonly",
|
||||
"clearParsedCache": "readonly",
|
||||
"closeMoreMenu": "readonly",
|
||||
"closeNav": "readonly",
|
||||
"comparePacketSets": "readonly",
|
||||
"computeBreakdownRanges": "readonly",
|
||||
"computeOverlapStats": "readonly",
|
||||
"connectWS": "readonly",
|
||||
"copyToClipboard": "readonly",
|
||||
"createColoredHexDump": "readonly",
|
||||
"currentPage": "readonly",
|
||||
"currentSkewValue": "readonly",
|
||||
"debounce": "readonly",
|
||||
"debouncedOnWS": "readonly",
|
||||
"destroy": "readonly",
|
||||
"devicePixelRatio": "readonly",
|
||||
"dispatchEvent": "readonly",
|
||||
"drawPacketRoute": "readonly",
|
||||
"escapeHtml": "readonly",
|
||||
"exports": "readonly",
|
||||
"favStar": "readonly",
|
||||
"filterPacketsByRoute": "readonly",
|
||||
"formatAbsoluteTimestamp": "readonly",
|
||||
"formatChartAxisLabel": "readonly",
|
||||
"formatDistance": "readonly",
|
||||
"formatDistanceRound": "readonly",
|
||||
"formatDrift": "readonly",
|
||||
"formatEngineBadge": "readonly",
|
||||
"formatHex": "readonly",
|
||||
"formatIsoLike": "readonly",
|
||||
"formatSkew": "readonly",
|
||||
"formatTimestamp": "readonly",
|
||||
"formatTimestampCustom": "readonly",
|
||||
"formatTimestampWithTooltip": "readonly",
|
||||
"formatVersionBadge": "readonly",
|
||||
"getDistanceUnit": "readonly",
|
||||
"getFavorites": "readonly",
|
||||
"getHashParams": "readonly",
|
||||
"getHealthThresholds": "readonly",
|
||||
"getNodeStatus": "readonly",
|
||||
"getParsedDecoded": "readonly",
|
||||
"getParsedPath": "readonly",
|
||||
"getPathLenOffset": "readonly",
|
||||
"getResolvedPath": "readonly",
|
||||
"getTileUrl": "readonly",
|
||||
"getTimestampCustomFormat": "readonly",
|
||||
"getTimestampFormatPreset": "readonly",
|
||||
"getTimestampMode": "readonly",
|
||||
"getTimestampTimezone": "readonly",
|
||||
"global": "readonly",
|
||||
"initGeoFilterOverlay": "readonly",
|
||||
"initTabBar": "readonly",
|
||||
"invalidateApiCache": "readonly",
|
||||
"isFavorite": "readonly",
|
||||
"isTransportRoute": "readonly",
|
||||
"makeColumnsResizable": "readonly",
|
||||
"makeRoleMarkerSVG": "readonly",
|
||||
"miniMarkdown": "readonly",
|
||||
"module": "readonly",
|
||||
"navigate": "readonly",
|
||||
"observerSkewSeverity": "readonly",
|
||||
"offWS": "readonly",
|
||||
"onWS": "readonly",
|
||||
"pad2": "readonly",
|
||||
"pad3": "readonly",
|
||||
"pages": "readonly",
|
||||
"payloadTypeColor": "readonly",
|
||||
"payloadTypeName": "readonly",
|
||||
"process": "readonly",
|
||||
"pullReconnect": "readonly",
|
||||
"qrcode": "readonly",
|
||||
"registerPage": "readonly",
|
||||
"renderSkewBadge": "readonly",
|
||||
"renderSkewSparkline": "readonly",
|
||||
"require": "readonly",
|
||||
"routeLayer": "readonly",
|
||||
"routeTypeName": "readonly",
|
||||
"setupPullToReconnect": "readonly",
|
||||
"syncBadgeColors": "readonly",
|
||||
"timeAgo": "readonly",
|
||||
"toggleFavorite": "readonly",
|
||||
"transportBadge": "readonly",
|
||||
"truncate": "readonly",
|
||||
"ws": "readonly",
|
||||
"wsListeners": "readonly"
|
||||
},
|
||||
"rules": {
|
||||
"no-undef": "error",
|
||||
"no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,14 @@ jobs:
|
||||
node test-issue-1364-pill-no-clamp.js
|
||||
node test-issue-1375-scope-stats-fetch.js
|
||||
|
||||
- name: 🧹 Frontend lint (eslint no-undef) — issue #1342
|
||||
run: |
|
||||
set -e
|
||||
# Use eslint@8 (legacy .eslintrc.json). Don't migrate to flat-config / eslint@9.
|
||||
# --no-save: avoid touching package.json / no committed node_modules.
|
||||
npm install --no-save --no-audit --no-fund eslint@8
|
||||
npx eslint public/*.js
|
||||
|
||||
- name: Verify proto syntax
|
||||
run: |
|
||||
set -e
|
||||
|
||||
+4
-2
@@ -20,8 +20,10 @@
|
||||
let userHasMoved = false;
|
||||
let controlsCollapsed = false;
|
||||
|
||||
// Safe escape — falls back to identity if app.js hasn't loaded yet
|
||||
const safeEsc = (typeof esc === 'function') ? esc : function (s) { return s; };
|
||||
// Safe escape — falls back to identity if app.js hasn't loaded yet.
|
||||
// Note: `esc` is not a true global; some IIFEs define it locally. Reference
|
||||
// through globalThis so the optional lookup is safe under `no-undef`.
|
||||
const safeEsc = (typeof globalThis.esc === 'function') ? globalThis.esc : function (s) { return s; };
|
||||
|
||||
// Roles loaded from shared roles.js (ROLE_STYLE, ROLE_LABELS, ROLE_COLORS globals)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user