developit / undom

🍩 1kb minimally viable DOM Document implementation
https://npm.im/undom
MIT License
671 stars 25 forks source link

Support D3? #3

Open jfsiii opened 8 years ago

jfsiii commented 8 years ago

Part of the reason I created #1 was I wondered about using this to render d3.

I was reading the https://github.com/Olical/react-faux-dom/ source when I saw your Tweet about this project.

I'm not sure exactly what changes are required. I believe some will be small (like ownerDocument) or namespaceURI while others (like style and querySelector) will require more time and bytes.

FWIW, here's their DOM support and their d3 tests. A key first step would be to get d3.select(document.createElement('div')).append('p').node() to return to newly created <p></p>.

It seems like we can either a) add support for this use case into this document b) create another project which imports undom. Do you have a preference? Does this use case (with limitations) seem like a fit for undom "core" or should it be an separate lib?

developit commented 8 years ago

I think querySelector() / querySelectorAll() would be nice to have, but it seems like the kind of thing that could just be bolted on as part of setup (or in a wrapper module?). I have a couple different implementations of selector engines lying around, but nothing I am able to properly open-source, given the added maintenance overhead.

For the other features - namespaceURI is an easy one for sure, it's already there just using the wrong name (my bad!). Style should be fairly easy, though I'm curious if there is a way to get around implementing the entire interface considering most stuff just uses property access. Generation of style.cssText / the style attribute based on style.* should be pretty minimal, I think Preact's version of that was around 100 bytes. The inverse can probably be done similarly lightweight by using a simplified parser that may not account for 100% of the various ill-formatted value normalizations & edge cases.

The other complex item on that list would be get*ClientRect() and the other layout calculations. These could be an add-on, or perhaps included in the repo as separate modules to be imported.

It might be nice to establish an extension mechanism, even it it's as simple as an Express-style .use() that just invokes plugins on a created document:

import undom from 'undom';
import mutationObserver from 'undom/mutation-observer';
import querySelector from 'undom/query-selector';
import layout from 'undom/layout';
import serialization from 'undom/serialization';

let document = undom()
    .use(mutationObserver)
    .use(querySelector)
    .use(layout)
    .use(serialization);

// or even just:
let document = undom([
  mutationObserver,
  querySelector,
  layout,
  serialization
]);

For those using ES Module bundlers with tree-shaking, we could have the ES Modules source provide re-exports of the various plugins/middleware:

import undom, { mutations, selectors, layout, serialization } from 'undom';

let document = undom([
  mutations,
  selectors,
  layout,
  serialization
]);

// ^ this could also be provided as a convenience loader:
import undom from 'undom/all';
let document = undom();
jfsiii commented 8 years ago

I was thinking of a very similar API for opting-in to various features. You got so much done in so little code it'd be a shame to lose the simple & small option (which satisfied your original use case) as more features are added. At the same time, it'd be a shame to have to pull in all of jsdom if you don't need the full BOM.

I like the idea of a undom-d3 project which build on the small core of undom. That way, any branches required for d3 patterns or bugs can be limited to that project.

Great point about mixing in querySelector{,All}. The one react-faux-dom uses, query-selector is the smallest (22KB/8KB) I've seen and has solid tests, IMO.

FWIW, I think getComputedStyle & getBoundingClientRect are "we fixed the glitch"-level code.

developit commented 8 years ago

Ah yeah, I figured those layout methods were basically faked. The recursive nature of layout computation seems like overkill for anything aiming to be lightweight.

I pulled MutationObserver from core in order to keep things light, so I'll probably submit a PR with the architecture we came to here and add MutationObserver as the first bundled plugin.

Any thoughts as to whether we should go with separate NPM packages (undom-mutationobserver, undom-selectors) versus a meta-package like above? I'm sort-of leaning towards the metapackage in order to keep versioning sane.

jfsiii commented 8 years ago

metapackage sounds good to me

jdarling commented 8 years ago

Just stumbled across udom. While it's a complete nightmare to maintain I prefer individual packages over metapackages. Why, it encourages others to help maintain, keeps bugs with their owner, means I only have to download/install the parts I need, and it seems to be "way" most of the Node world is working now.

The hard decision is what is part of the udom core, and what belongs in an extension. Of course you can also have metapackage's and support for external packages gaining the best of both worlds as long as udom doesn't bloat :D

developit commented 8 years ago

One option there would be to have a monorepo (perhaps set up via lerna), but publish the packages separately (undom-foo, etc). That would centralize development and versioning for whatever list of "core" modules we end up with, but keep the installs light (1 file).

jfsiii commented 8 years ago

TBH, monorepo + Lerna was my first reaction, but I wanted to prevent a tangent/bikeshed.

I'm ok with it if you are. It's my preferred approach but I haven't used Learna in any of my existing monorepi.

developit commented 8 years ago

Same. I can take a stab at the initial setup if that seems like a good approach.