ramnathv / htmlwidgets

HTML Widgets for R
http://htmlwidgets.org
Other
792 stars 205 forks source link

fix: Restores eager recursion into `options` objects #478

Closed gadenbuie closed 12 months ago

gadenbuie commented 12 months ago

Given the disruption caused by #467 and the innumerable ways that people may have unknowingly been relying on htmlwidget's previously eager recursion into any list-like object, this PR restores previous behavior.

Both #466 and https://github.com/rstudio/DT/issues/1092 provide examples why shouldEval() shouldn't recurse into arbitrary objects -- while uncommon, it's possible to run into cases where an object is infinitely recursive.

On the other hand, https://github.com/rstudio/leaflet/issues/891 and https://github.com/react-R/reactR/issues/82 show two cases where eager recursion was a feature, not a bug:

  1. In https://github.com/rstudio/leaflet/issues/891, issues arise from using something akin to structure(list(), class = "custom_options")). This is a common way to create a list-like options argument. The problem is that structure() doesn't include the "list" class when the class argument is provided, and therefore the code introduced in #467 would not recurse into these objects. We could do one level of recursion for list-like, but not "list" inherting, objects, but...
  2. https://github.com/react-R/reactR/issues/82 shows that a custom, non-list classed object isn't a reliable signal of how far a package author expects htmlwidgets to recurse. In reactR's case, AFAICT, there's an expectation that JS() could be used on htmltools tag() attributes, which could be at any part of the HTML tree, and where the outer shiny.tag doesn't include a "list" class.

This PR will at least fix #477 and likely many other places we have yet to discover.

While it seems reasonable to carve out an exception for POSIXlt as in #473, both that PR and #466 indicate that there are likely many object classes that could require an exception. It's not clear to me that htmlwidgets should go down the path of carving out specific exceptions, especially considering that shouldEval() is called on an object that is in the process of moving from R to JSON, where these objects need to be serialized as common JSON types anyway.