leopard-js / sb-edit

Javascript library for manipulating Scratch project files
MIT License
55 stars 15 forks source link

toLeopard: Avoid overwriting any browser globals, perhaps #138

Open towerofnix opened 6 months ago

towerofnix commented 6 months ago

From @PullJosh: https://github.com/leopard-js/sb-edit/pull/137#discussion_r1614635282

Question: Do we want to blacklist browser-provided identifiers like Array, Audio, Error, File, Map, Set, Temporal, etc., which are not used in generated Leopard code but could cause conflicts or confusion when the user starts adding their own hand-written JS code?

There are a lot of them. And keeping up-to-date might be challenging.

We're really interested in this point. It's true that with the new PR, we avoid overwriting globals which in-scope generated Leopard code depends on, fixing certain project runtime bugs when the fates align poorly (#133).

But that's not the only reason to avoid overwriting globals. Like PullJosh wrote, users who are learning JavaScript will start to use lots of browser-provided globals. (And likely pretty quickly! BTW, Object is missing from your list ✨) It would be nice if generated code never overshadowed at least the popular ones.

It would be pretty snazzy if the Leopard editor gracefully helped you rename sprites, but in the meanwhile (and certainly if you're working in your own text editor) it can be awkward to change a generates sprite named Array over to, well, some other name, when you want to start using array utilities... in the sprite... that is named Array. (You know, after you've introduced code in other sprites that already uses Array, so now the string "Array" means two totally different things in your overall project code.)


Some implementation thoughts:

Here are all the first-letter-capitalized names in globals.browser. `AbortController AbortSignal AbsoluteOrientationSensor AbstractRange Accelerometer AnalyserNode Animation AnimationEffect AnimationEvent AnimationPlaybackEvent AnimationTimeline Attr Audio AudioBuffer AudioBufferSourceNode AudioContext AudioData AudioDecoder AudioDestinationNode AudioEncoder AudioListener AudioNode AudioParam AudioParamMap AudioProcessingEvent AudioScheduledSourceNode AudioSinkInfo AudioWorklet AudioWorkletGlobalScope AudioWorkletNode AudioWorkletProcessor AuthenticatorAssertionResponse AuthenticatorAttestationResponse AuthenticatorResponse BackgroundFetchManager BackgroundFetchRecord BackgroundFetchRegistration BarProp BaseAudioContext BatteryManager BeforeUnloadEvent BiquadFilterNode Blob BlobEvent Bluetooth BluetoothCharacteristicProperties BluetoothDevice BluetoothRemoteGATTCharacteristic BluetoothRemoteGATTDescriptor BluetoothRemoteGATTServer BluetoothRemoteGATTService BluetoothUUID BroadcastChannel BrowserCaptureMediaStreamTrack ByteLengthQueuingStrategy Cache CacheStorage CanvasCaptureMediaStream CanvasCaptureMediaStreamTrack CanvasGradient CanvasPattern CanvasRenderingContext2D CaptureController CaretPosition CDATASection ChannelMergerNode ChannelSplitterNode CharacterBoundsUpdateEvent CharacterData Clipboard ClipboardEvent ClipboardItem CloseEvent Comment CompositionEvent CompressionStream ConstantSourceNode ContentVisibilityAutoStateChangeEvent ConvolverNode CookieChangeEvent CookieDeprecationLabel CookieStore CookieStoreManager CountQueuingStrategy Credential CredentialsContainer CropTarget Crypto CryptoKey CSS CSSAnimation CSSConditionRule CSSContainerRule CSSCounterStyleRule CSSFontFaceRule CSSFontFeatureValuesRule CSSFontPaletteValuesRule CSSGroupingRule CSSImageValue CSSImportRule CSSKeyframeRule CSSKeyframesRule CSSKeywordValue CSSLayerBlockRule CSSLayerStatementRule CSSMathClamp CSSMathInvert CSSMathMax CSSMathMin CSSMathNegate CSSMathProduct CSSMathSum CSSMathValue CSSMatrixComponent CSSMediaRule CSSNamespaceRule CSSNumericArray CSSNumericValue CSSPageRule CSSPerspective CSSPositionTryDescriptors CSSPositionTryRule CSSPositionValue CSSPropertyRule CSSRotate CSSRule CSSRuleList CSSScale CSSScopeRule CSSSkew CSSSkewX CSSSkewY CSSStartingStyleRule CSSStyleDeclaration CSSStyleRule CSSStyleSheet CSSStyleValue CSSSupportsRule CSSTransformComponent CSSTransformValue CSSTransition CSSTranslate CSSUnitValue CSSUnparsedValue CSSVariableReferenceValue CustomElementRegistry CustomEvent CustomStateSet DataTransfer DataTransferItem DataTransferItemList DecompressionStream DelayNode DelegatedInkTrailPresenter DeviceMotionEvent DeviceMotionEventAcceleration DeviceMotionEventRotationRate DeviceOrientationEvent Document DocumentFragment DocumentPictureInPicture DocumentPictureInPictureEvent DocumentTimeline DocumentType DOMError DOMException DOMImplementation DOMMatrix DOMMatrixReadOnly DOMParser DOMPoint DOMPointReadOnly DOMQuad DOMRect DOMRectList DOMRectReadOnly DOMStringList DOMStringMap DOMTokenList DragEvent DynamicsCompressorNode EditContext Element ElementInternals EncodedAudioChunk EncodedVideoChunk ErrorEvent Event EventCounts EventSource EventTarget External EyeDropper FeaturePolicy FederatedCredential Fence FencedFrameConfig FetchLaterResult File FileList FileReader FileSystem FileSystemDirectoryEntry FileSystemDirectoryHandle FileSystemDirectoryReader FileSystemEntry FileSystemFileEntry FileSystemFileHandle FileSystemHandle FileSystemWritableFileStream FocusEvent FontData FontFace FontFaceSet FontFaceSetLoadEvent FormData FormDataEvent FragmentDirective GainNode Gamepad GamepadAxisMoveEvent GamepadButton GamepadButtonEvent GamepadEvent GamepadHapticActuator GamepadPose Geolocation GeolocationCoordinates GeolocationPosition GeolocationPositionError GPU GPUAdapter GPUAdapterInfo GPUBindGroup GPUBindGroupLayout GPUBuffer GPUBufferUsage GPUCanvasContext GPUColorWrite GPUCommandBuffer GPUCommandEncoder GPUCompilationInfo GPUCompilationMessage GPUComputePassEncoder GPUComputePipeline GPUDevice GPUDeviceLostInfo GPUError GPUExternalTexture GPUInternalError GPUMapMode GPUOutOfMemoryError GPUPipelineError GPUPipelineLayout GPUQuerySet GPUQueue GPURenderBundle GPURenderBundleEncoder GPURenderPassEncoder GPURenderPipeline GPUSampler GPUShaderModule GPUShaderStage GPUSupportedFeatures GPUSupportedLimits GPUTexture GPUTextureUsage GPUTextureView GPUUncapturedErrorEvent GPUValidationError GravitySensor Gyroscope HashChangeEvent Headers HID HIDConnectionEvent HIDDevice HIDInputReportEvent Highlight HighlightRegistry History HTMLAllCollection HTMLAnchorElement HTMLAreaElement HTMLAudioElement HTMLBaseElement HTMLBodyElement HTMLBRElement HTMLButtonElement HTMLCanvasElement HTMLCollection HTMLDataElement HTMLDataListElement HTMLDetailsElement HTMLDialogElement HTMLDirectoryElement HTMLDivElement HTMLDListElement HTMLDocument HTMLElement HTMLEmbedElement HTMLFencedFrameElement HTMLFieldSetElement HTMLFontElement HTMLFormControlsCollection HTMLFormElement HTMLFrameElement HTMLFrameSetElement HTMLHeadElement HTMLHeadingElement HTMLHRElement HTMLHtmlElement HTMLIFrameElement HTMLImageElement HTMLInputElement HTMLLabelElement HTMLLegendElement HTMLLIElement HTMLLinkElement HTMLMapElement HTMLMarqueeElement HTMLMediaElement HTMLMenuElement HTMLMetaElement HTMLMeterElement HTMLModElement HTMLObjectElement HTMLOListElement HTMLOptGroupElement HTMLOptionElement HTMLOptionsCollection HTMLOutputElement HTMLParagraphElement HTMLParamElement HTMLPictureElement HTMLPreElement HTMLProgressElement HTMLQuoteElement HTMLScriptElement HTMLSelectElement HTMLSlotElement HTMLSourceElement HTMLSpanElement HTMLStyleElement HTMLTableCaptionElement HTMLTableCellElement HTMLTableColElement HTMLTableElement HTMLTableRowElement HTMLTableSectionElement HTMLTemplateElement HTMLTextAreaElement HTMLTimeElement HTMLTitleElement HTMLTrackElement HTMLUListElement HTMLUnknownElement HTMLVideoElement IDBCursor IDBCursorWithValue IDBDatabase IDBFactory IDBIndex IDBKeyRange IDBObjectStore IDBOpenDBRequest IDBRequest IDBTransaction IDBVersionChangeEvent IdentityCredential IdentityCredentialError IdentityProvider IdleDeadline IdleDetector IIRFilterNode Image ImageBitmap ImageBitmapRenderingContext ImageCapture ImageData ImageDecoder ImageTrack ImageTrackList Ink InputDeviceCapabilities InputDeviceInfo InputEvent IntersectionObserver IntersectionObserverEntry Iterator Keyboard KeyboardEvent KeyboardLayoutMap KeyframeEffect LargestContentfulPaint LaunchParams LaunchQueue LayoutShift LayoutShiftAttribution LinearAccelerationSensor Location Lock LockManager MathMLElement MediaCapabilities MediaCapabilitiesInfo MediaDeviceInfo MediaDevices MediaElementAudioSourceNode MediaEncryptedEvent MediaError MediaKeyError MediaKeyMessageEvent MediaKeys MediaKeySession MediaKeyStatusMap MediaKeySystemAccess MediaList MediaMetadata MediaQueryList MediaQueryListEvent MediaRecorder MediaRecorderErrorEvent MediaSession MediaSource MediaSourceHandle MediaStream MediaStreamAudioDestinationNode MediaStreamAudioSourceNode MediaStreamEvent MediaStreamTrack MediaStreamTrackAudioSourceNode MediaStreamTrackAudioStats MediaStreamTrackEvent MediaStreamTrackGenerator MediaStreamTrackProcessor MediaStreamTrackVideoStats MessageChannel MessageEvent MessagePort MIDIAccess MIDIConnectionEvent MIDIInput MIDIInputMap MIDIMessageEvent MIDIOutput MIDIOutputMap MIDIPort MimeType MimeTypeArray ModelGenericSession ModelManager MouseEvent MutationEvent MutationObserver MutationRecord NamedNodeMap NavigateEvent Navigation NavigationActivation NavigationCurrentEntryChangeEvent NavigationDestination NavigationHistoryEntry NavigationPreloadManager NavigationTransition Navigator NavigatorLogin NavigatorManagedData NavigatorUAData NetworkInformation Node NodeFilter NodeIterator NodeList Notification NotifyPaintEvent NotRestoredReasonDetails NotRestoredReasons OfflineAudioCompletionEvent OfflineAudioContext OffscreenCanvas OffscreenCanvasRenderingContext2D Option OrientationSensor OscillatorNode OTPCredential OverconstrainedError PageRevealEvent PageSwapEvent PageTransitionEvent PannerNode PasswordCredential Path2D PaymentAddress PaymentManager PaymentMethodChangeEvent PaymentRequest PaymentRequestUpdateEvent PaymentResponse Performance PerformanceElementTiming PerformanceEntry PerformanceEventTiming PerformanceLongAnimationFrameTiming PerformanceLongTaskTiming PerformanceMark PerformanceMeasure PerformanceNavigation PerformanceNavigationTiming PerformanceObserver PerformanceObserverEntryList PerformancePaintTiming PerformanceResourceTiming PerformanceScriptTiming PerformanceServerTiming PerformanceTiming PeriodicSyncManager PeriodicWave Permissions PermissionStatus PERSISTENT PictureInPictureEvent PictureInPictureWindow Plugin PluginArray PointerEvent PopStateEvent Presentation PresentationAvailability PresentationConnection PresentationConnectionAvailableEvent PresentationConnectionCloseEvent PresentationConnectionList PresentationReceiver PresentationRequest PressureObserver PressureRecord ProcessingInstruction Profiler ProgressEvent PromiseRejectionEvent ProtectedAudience PublicKeyCredential PushManager PushSubscription PushSubscriptionOptions RadioNodeList Range ReadableByteStreamController ReadableStream ReadableStreamBYOBReader ReadableStreamBYOBRequest ReadableStreamDefaultController ReadableStreamDefaultReader RelativeOrientationSensor RemotePlayback ReportingObserver Request ResizeObserver ResizeObserverEntry ResizeObserverSize Response RTCCertificate RTCDataChannel RTCDataChannelEvent RTCDtlsTransport RTCDTMFSender RTCDTMFToneChangeEvent RTCEncodedAudioFrame RTCEncodedVideoFrame RTCError RTCErrorEvent RTCIceCandidate RTCIceTransport RTCPeerConnection RTCPeerConnectionIceErrorEvent RTCPeerConnectionIceEvent RTCRtpReceiver RTCRtpScriptTransform RTCRtpSender RTCRtpTransceiver RTCSctpTransport RTCSessionDescription RTCStatsReport RTCTrackEvent Scheduler Scheduling Screen ScreenDetailed ScreenDetails ScreenOrientation ScriptProcessorNode ScrollTimeline SecurityPolicyViolationEvent Selection Sensor SensorErrorEvent Serial SerialPort ServiceWorker ServiceWorkerContainer ServiceWorkerRegistration ShadowRoot SharedStorage SharedStorageWorklet SharedWorker SourceBuffer SourceBufferList SpeechSynthesis SpeechSynthesisErrorEvent SpeechSynthesisEvent SpeechSynthesisUtterance SpeechSynthesisVoice StaticRange StereoPannerNode Storage StorageBucket StorageBucketManager StorageEvent StorageManager StylePropertyMap StylePropertyMapReadOnly StyleSheet StyleSheetList SubmitEvent SubtleCrypto SVGAElement SVGAngle SVGAnimatedAngle SVGAnimatedBoolean SVGAnimatedEnumeration SVGAnimatedInteger SVGAnimatedLength SVGAnimatedLengthList SVGAnimatedNumber SVGAnimatedNumberList SVGAnimatedPreserveAspectRatio SVGAnimatedRect SVGAnimatedString SVGAnimatedTransformList SVGAnimateElement SVGAnimateMotionElement SVGAnimateTransformElement SVGAnimationElement SVGCircleElement SVGClipPathElement SVGComponentTransferFunctionElement SVGDefsElement SVGDescElement SVGElement SVGEllipseElement SVGFEBlendElement SVGFEColorMatrixElement SVGFEComponentTransferElement SVGFECompositeElement SVGFEConvolveMatrixElement SVGFEDiffuseLightingElement SVGFEDisplacementMapElement SVGFEDistantLightElement SVGFEDropShadowElement SVGFEFloodElement SVGFEFuncAElement SVGFEFuncBElement SVGFEFuncGElement SVGFEFuncRElement SVGFEGaussianBlurElement SVGFEImageElement SVGFEMergeElement SVGFEMergeNodeElement SVGFEMorphologyElement SVGFEOffsetElement SVGFEPointLightElement SVGFESpecularLightingElement SVGFESpotLightElement SVGFETileElement SVGFETurbulenceElement SVGFilterElement SVGForeignObjectElement SVGGElement SVGGeometryElement SVGGradientElement SVGGraphicsElement SVGImageElement SVGLength SVGLengthList SVGLinearGradientElement SVGLineElement SVGMarkerElement SVGMaskElement SVGMatrix SVGMetadataElement SVGMPathElement SVGNumber SVGNumberList SVGPathElement SVGPatternElement SVGPoint SVGPointList SVGPolygonElement SVGPolylineElement SVGPreserveAspectRatio SVGRadialGradientElement SVGRect SVGRectElement SVGScriptElement SVGSetElement SVGStopElement SVGStringList SVGStyleElement SVGSVGElement SVGSwitchElement SVGSymbolElement SVGTextContentElement SVGTextElement SVGTextPathElement SVGTextPositioningElement SVGTitleElement SVGTransform SVGTransformList SVGTSpanElement SVGUnitTypes SVGUseElement SVGViewElement SyncManager TaskAttributionTiming TaskController TaskPriorityChangeEvent TaskSignal TEMPORARY Text TextDecoder TextDecoderStream TextEncoder TextEncoderStream TextEvent TextFormat TextFormatUpdateEvent TextMetrics TextTrack TextTrackCue TextTrackCueList TextTrackList TextUpdateEvent TimeEvent TimeRanges ToggleEvent Touch TouchEvent TouchList TrackEvent TransformStream TransformStreamDefaultController TransitionEvent TreeWalker TrustedHTML TrustedScript TrustedScriptURL TrustedTypePolicy TrustedTypePolicyFactory UIEvent URL URLPattern URLSearchParams USB USBAlternateInterface USBConfiguration USBConnectionEvent USBDevice USBEndpoint USBInterface USBInTransferResult USBIsochronousInTransferPacket USBIsochronousInTransferResult USBIsochronousOutTransferPacket USBIsochronousOutTransferResult USBOutTransferResult UserActivation ValidityState VideoColorSpace VideoDecoder VideoEncoder VideoFrame VideoPlaybackQuality ViewTimeline ViewTransition ViewTransitionTypeSet VirtualKeyboard VirtualKeyboardGeometryChangeEvent VisibilityStateEntry VisualViewport VTTCue VTTRegion WakeLock WakeLockSentinel WaveShaperNode WebAssembly WebGL2RenderingContext WebGLActiveInfo WebGLBuffer WebGLContextEvent WebGLFramebuffer WebGLProgram WebGLQuery WebGLRenderbuffer WebGLRenderingContext WebGLSampler WebGLShader WebGLShaderPrecisionFormat WebGLSync WebGLTexture WebGLTransformFeedback WebGLUniformLocation WebGLVertexArrayObject WebSocket WebSocketError WebSocketStream WebTransport WebTransportBidirectionalStream WebTransportDatagramDuplexStream WebTransportError WebTransportReceiveStream WebTransportSendStream WGSLLanguageFeatures WheelEvent Window WindowControlsOverlay WindowControlsOverlayGeometryChangeEvent Worker Worklet WorkletGlobalScope WritableStream WritableStreamDefaultController WritableStreamDefaultWriter XMLDocument XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload XMLSerializer XPathEvaluator XPathExpression XPathResult XRAnchor XRAnchorSet XRBoundedReferenceSpace XRCamera XRCPUDepthInformation XRDepthInformation XRDOMOverlayState XRFrame XRHitTestResult XRHitTestSource XRInputSource XRInputSourceArray XRInputSourceEvent XRInputSourcesChangeEvent XRLayer XRLightEstimate XRLightProbe XRPose XRRay XRReferenceSpace XRReferenceSpaceEvent XRRenderState XRRigidTransform XRSession XRSessionEvent XRSpace XRSystem XRTransientInputHitTestResult XRTransientInputHitTestSource XRView XRViewerPose XRViewport XRWebGLBinding XRWebGLDepthInformation XRWebGLLayer XSLTProcessor`
And here are just the ones under globals.builtin. (Please note that this is not a subset of globals.browser, i.e. we need to combine them if we want to use both.) `AggregateError Array ArrayBuffer Atomics BigInt BigInt64Array BigUint64Array Boolean DataView Date Error EvalError FinalizationRegistry Float32Array Float64Array Function Infinity Int16Array Int32Array Int8Array Intl JSON Map Math NaN Number Object Promise Proxy RangeError ReferenceError Reflect RegExp Set SharedArrayBuffer String Symbol SyntaxError TypeError Uint16Array Uint32Array Uint8Array Uint8ClampedArray URIError WeakMap WeakRef WeakSet`

(Note that the list above includes recent additions like AggregateError.)

So we have a few practical options:

  1. Just hard-code our own list, altogether, but more expansive than currently. Admit that it's not intended to be comprehensive. May be a subset of globals.builtin or a subset of the union [globals.builtin, globals.browser].
  2. Hard-code our own subset of globals.builtin and combine that with globals.browser in its entirety.
  3. Use globals.builtin in its entirety, don't bother with globals.browser.
  4. Use the union of globals.builtin and globals.browser in its entirety.

I'm leaning towards the "don't hard-code anything" options, but could go either way on including globals.browser. There are a lot of names in that list, and most of them are extremely domain-specific. There's a decent chance that users themselves might code new Sprite subclasses with those names, and never run into a problem for it! But do feel free to convince us that globals.browser should totally be part of the reserved sprite name list, if you think that's right. Or share some totally different direction, we're always interested! 🥳

PullJosh commented 6 months ago

Should we block browser globals? My argument on this is that we'd like to help users write "good" JavaScript code. Of course, "good" is subjective, and there's no one right answer. But creating a class called Array that has nothing to do with JavaScript arrays seems bad. ArraySprite seems like a good compromise that a reasonable human might actually write and is easy to create an sb-edit rule for.

Which globals should we block? Looking at the lists of known globals, I think it would be overkill to block all of them. For example, if a human author were to write their own class called StorageBucket, I would have no objections to that. And I don't think sb-edit needs to either.

Can we generate (and update) the list automatically? I think it's a really, really good idea. My memory says that the most frequent kind of bug we've had to fix in sb-edit thus far is failing to update this list in response to changes in Leopard. To be honest, I think we could write a little script that pulls in Leopard's source code, parses it, and extracts all the method names.

If you accept my previous point–that we should only block some browser globals–then updating the browser globals list is a different beast. Exactly which classes are reasonable to override is totally a vibes-based decision. If we want to automate the process of retrieving/generating these lists, we could probably come up with a metric that approximately matches our own personal ✨vibes✨. I propose a metric based on how commonly different keywords appear in JavaScript code on the web. I'm not sure if that data exists anywhere.

towerofnix commented 6 months ago

To be honest, I think we could write a little script that pulls in Leopard's source code, parses it, and extracts all the method names.

We agree, but we aren't sure that source code parsing is an effective approach. We don't have experience with crawling an AST (although perhaps you do). We would rather import the leopard module outright and crawl its exposed classes directly.

Example script (this works in the Node.js REPL!) ```js leopard = await import("leopard"); classNames = Object.keys(leopard.default); /* [ 'Color', 'Costume', 'Project', 'Sound', 'Sprite', 'Stage', 'Trigger', 'Watcher' ] */ getProtoProperties = proto => (proto ? Object.getOwnPropertyNames(proto) .concat(getProtoProperties(Object.getPrototypeOf(proto))) : []); spriteProperties = getProtoProperties(leopard.default.Sprite.prototype).sort() /* [ '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'andClones', 'answer', 'arrayIncludes', 'askAndWait', 'askAndWait', 'broadcast', 'broadcastAndWait', 'clearPen', 'colorTouching', 'compare', 'constructor', 'constructor', 'constructor', 'costume', 'costumeNumber', 'createClone', 'degToRad', 'degToScratch', 'deleteThisClone', 'direction', 'getSound', 'getSoundsPlayedByMe', 'glide', 'goto', 'hasOwnProperty', 'ifOnEdgeBounce', 'indexInArray', 'isPrototypeOf', 'itemOf', 'keyPressed', 'letterOf', 'loudness', 'mouse', 'move', 'moveAhead', 'moveBehind', 'nearestEdge', 'normalizeDeg', 'penColor', 'penDown', 'playSoundUntilDone', 'positionInFence', 'propertyIsEnumerable', 'radToDeg', 'radToScratch', 'random', 'restartTimer', 'say', 'sayAndWait', 'scratchTan', 'scratchToDeg', 'scratchToRad', 'sprites', 'stage', 'stamp', 'startSound', 'stopAllOfMySounds', 'stopAllSounds', 'stringIncludes', 'think', 'thinkAndWait', 'timer', 'toBoolean', 'toLocaleString', 'toNumber', 'toString', 'toString', 'touching', 'valueOf', 'vars', 'wait', 'warp', 'wrapClamp', 'x', 'y' ] */ ```

If we want to automate the process of retrieving/generating these lists, we could probably come up with a metric that approximately matches our own personal ✨vibes✨. I propose a metric based on how commonly different keywords appear in JavaScript code on the web. I'm not sure if that data exists anywhere.

We're OK in principle with a "ranking" or "popularity" usage based on keyword usage online. We swear W3C and TC39 (web API and ECMAScript standards organizations) make fairly frequent use of a tool that hooks into exactly this data, but we can't figure out the name of it right now! We'll look into it.

We think that using this tool will be 100% based on, you know, how practical it is to hook into. Obviously if there's a periodically updated npm package that just says how popular JavaScript words are, that would be great. It's mainly a question of practicality. We don't want to introduce a system that could have a lot of edge cases or maintenance upkeep, even if it gives us a nice quantified metric ("used in >1.5% of pages processed by [tool]", etc).

If it's just not practical, then we're opposed to using the full globals.browser list. We'd rather use no names to hard-coding any names, but if it appears necessary to use a subset of globals.browser and there doesn't seem to be a good reliable way to generate that subset, we would cut our losses and hard-code it.

Important reminder that globals.browser is not a superset of globals.builtin, which we strongly believe should be included in full without any vibe-based conditions. Except that "we should include all of it" is vibe-based anyway LOL. ECMAScript receives new features only once a year and generally expects new APIs to be developed over a long period if time. The baseline is that most of it is functionally helpful for most JavaScript developers, regardless the product discipline (they're lower-level / more basic APIs, you know, compared to web APIs). We think it'd be nice for sb-edit to stay on top of new interfaces, and we don't think its comparatively minimal list of names would get in the way of sprites that users want to create (or rather port from existing JavaScript projects).

Maybe there would be a case in the Leopard editor to warn users that the sprite name (or global variable) that they have created overrides the same name as a "popular" JavaScript global? sb-edit can only affect a Leopard project at the time of its generation (and through the generated code's practical consequences), so it doesn't address e.g. a user deciding to create a sprite named Object themselves down the line, unaware that Object is a useful global to have available and certainly not replace with some other value (even if it's just in your own code).