Make features more declarative #4779
fregante posted onGitHub
Given the trouble we're having with ajaxed features and the increasingly-dynamic nature of GitHub, I'm starting to think we should have a better way to define features.
selector-observer
gets us almost there, but it awaits Dom-ready so we can't use it all the time.
For example if we expect a tab to be added on the tab bar, I'd expect to write something like:
register({
include: [isRepo],
selector: 'xyz',
action: 'append',
ourClass: 'rgh-releases-tab'
element: async () => <a href={…} class="rgh-releases-tab">Releases</a>,
})
And the feature manager would automatically look for selector
, on isRepo
, and append
the element
unless ourClass
already exists at that location.
This basically means we we'll have to use MutationObserver with subtree: true
… but oh well.
Ideally though, this would be even higher level:
register({
location: 'repo-tab',
element: async () => <a href={…}>Releases</a>,
})
With locations/repo-tab.tsx
that knows exactly where that location appears (isRepo
), which selector to use and whether to append/prepend/insertBefore/etc. It could even detect the tab with its own auto-generated class.
For features that don't just add an element, there could be additional onInit
/onAdd
/onRemove
handlers.
There can also be a way to remove elements, like:
register({
location: 'repo-sidebar',
action: async () => { /*manually remove elements as usual*/},
})
The difference would be that the location definition would know how to handle the sidebar and know when to run action
(e.g. when the sidebar is regenerated)
Too complicated? 😬
If anyone wants to try this, you can start prototyping outside features/index
(no options, etc) just to see what it could look like and if it's feasible.
Technically this register
function could also just live inside our regular init
, even if it can't return false
, that's ok:
```js features.add({ init: () => { register(...) }, deduplicate: 'disabled', // Has its own logic, this string disables ajaxing completely awaitDomReady: false, // Has its own logic })