Epic: support multiple charting libraries via "engines"
- Dominant language
- Handlebars
- Stars
- 265
- Forks
- 236
- PR merge metrics
- No merged PRs in 30d
Description
Cedar v0 was written on top of vega/d3 and cedar v1 will be written on top of amCharts, yet, outside of `override` we've been able to keep much of the same API. We should in theory be able to be able isolate the implementation details specific to a given charting library into an "engine" that the consuming library can load depending on their needs.
## API
The likely API would look like:
```
const config = {
engine: 'vega', // defaults to 'amCharts'?
type: 'bar'
...
};
const chart = new Cedar(config);
chart.show("chartDiv"); // show() calls the appropriate render method from Cedar.vega or throws an error if that has not been loaded
```
In addition, we _may_ want to support:
- using more than one engine in a single app on a chart by chart basis
- dynamically loading the engine code at run time
## Packages, dependencies, and loading
The challenge is how do we allow consuming applications to only load the engine(s) that they need in their app.
### Option 1: The "Plugin" Model
The first options is that we each keep the current idea that engine is a separate package, but we invert the current dependency structure (where cedar depends on cedar-amcharts) so that each engine would depend on cedar (likely as a peer). The base (cedar) package would define a global namespace (`Cedar`), and any engines are also loaded would append themselves to that namespace (`Cedar.amCharts`, `Cedar.vega`), similar to the way that Leaflet and amCharts plugins work.
When using the UMD build:
```
```
Or if installing from the registry for use in a custom local build:
```
# NOTE: consuming apps can chose whether or not to treat amCharts as external
npm i amcharts3 @esri/cedar @esri/cedar-amcharts
```
Then you would include these lines in the consuming app:
```
import AmCharts from 'amcharts3'
import Cedar from '@esri/cedar';
// NOTE: only importing this for the side effect of appending to `Cedar.amCharts`
import '@esri/cedar-amcharts'
```
PROs:
- established pattern used by other libraries, well understood
CONs:
- consumers need to know what engine(s) they need and understand the dependencies
Also, not really a CON, but to me it is somewhat implicit that you can use the base package (cedar) w/o the plugin (like Leaflet), but in our case you can't. This is OK, I think it's how amCharts works (i.e. you need to add serial or xy to do anything).
### Option 2 One Cedar to Rule Them All
We could get rid of cedar-amcharts package and include its code within the cedar package. Future engines like cedar-vega would be added inside the cedar package as well. We'd then rely on something like [dynamic `import()` statements](http://2ality.com/2017/01/import-operator.html) to load whichever engine(s) are needed at runtime.
```
```
Or if installing from the registry for use in a local build:
```
# NOTE: consuming apps can chose whether or not to treat amCharts as external
npm i amcharts3 @esri/cedar
```
Then in the consuming app's code:
```
import AmCharts from 'amcharts3'
import Cedar from '@esri/cedar'
```
PROS:
- consuming apps don't need to know about or explicitly load engines, cedar handles this for them
CONS:
- While I'm pretty sure we can get dynamic `import()` statements or something similar to work in a CDN where we have full control over the build pipeline and distributed artifacts, I'm not sure we'd be able to set those up in a way that would work with every consuming app's build toolchain. We might even need to change our own toolchain just to get it working in our apps. While [TypeScript now supports dynamic import()](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html), unfortunately [rollup does not yet](https://github.com/rollup/rollup/issues/1325) and [nor does ember-cli](https://github.com/ember-cli/rfcs/pull/98). Maybe we'd have to swtich out rollup for webpack :man_facepalming:, or maybe drop rollup and just have TypeScript to generate the UMD build (since it's all one package) and _maybe_, just _maybe_ [whatever it spits out](https://github.com/Microsoft/TypeScript/issues/14495) will actually work in Ember after it's been `equireray`ed by one of the Esri addons... maybe.
If for whatever reason we really felt like we needed to have each engine in it's own package that would only complicate the above issues even further. So if separate packages is a requirement, I'd suggest we look into the Option 1 instead.
I hate to admit this, but [Dojo 1's AMD build with `staticHasFeatures`](https://dojotoolkit.org/documentation/tutorials/1.10/build/index.html#dead-code-path-removal) is the perfect solution to this problem. I'm somewhat encouraged by the fact that [Dojo2 is ditching their own dedicated loader now that `import()` has landed in TS](https://www.sitepen.com/blog/2017/06/28/typescript-2-4-dynamic-imports-and-weak-types/) and curious if/how they plan to make something like `staticHasFeatures` work w/ `import()` and webpack... but I digress. My point is, I think this idea is the way of the future, but it's [not the future yet](https://github.com/rollup/rollup/issues/372#issuecomment-294838279).
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.