Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZERO circular dependency #2429

Merged
merged 17 commits into from
Mar 2, 2018
Merged

ZERO circular dependency #2429

merged 17 commits into from
Mar 2, 2018

Conversation

etpinard
Copy link
Contributor

@etpinard etpinard commented Mar 1, 2018

resolves #236 and closes milestone/9.

I started working on this PR when I noticed that the new layout grid module from #2399 has a circular dependency:

image

This went unnoticed because some other PR(s) removed one or two circular dependencies. On master we now have:

image

whereas our test-syntax script was happy with 16 or less.


So after fixing the grid circular dep (that's in commit bee37dd), I tried to see what it would take to 🔪 all circular dependencies. In brief, we had:

and to my surprise this worked as a charm 🍀 🍾 🎉


Looking forward, removing these circular deps isn't just nice, it should allow us to trim our bundle size using browser-pack-flat or even tinyify. Preliminary attempts were positive: I think we'll be able to trim tens of kBs out of our bundles. I'll open up a new PR for this soon. Moreover, there were reports of some dev tools (the new FF ones I think) balking on our source code, this might help.

@alexcjohnson @nicolaskruchten hopefully you'll enjoy this. 😛

... so that its attributes are added to the schema
    free of circular deps.
- no need to have a separate method just for the top-level
  method.
... by using utilizing component-to-schema logic,
    instead of punching the error bar attributes into
    the trace declarations.
... so that down the road errorbars could be taken out
    or src/core.js, and be its own registrable module.
- export api methods in plot_api/index.js
- expose and register them in src/core.js
@nicolaskruchten
Copy link
Member

Anything that makes bundling easier, faster, smaller :)

@@ -19,8 +19,8 @@ var axisIDs = require('../plots/cartesian/axis_ids');
var Lib = require('../lib');
var _ = Lib._;
var Color = require('../components/color');
var Grid = require('../components/grid');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to go the last inch here and turn Grid.method into Registry.getComponentMethod? Then grid becomes an optional component, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in -> 9246fda

}, 'degauss')).toEqual([]);
});

it('returns an empty array if none present', function() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test case description copied from above

But also, if you leave these un-nested, the descriptions should reference the method being tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in e05f4f0


beforeAll(function() {
Registry.register(fakeModule);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to remove this in afterAll (here and in the next suite), as we do in the suite above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in e05f4f0

src/registry.js Outdated
*/
exports.call = function(name, args) {
return exports.apiMethodRegistry[name].apply(null, args);
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get rid of the array wrapping the args, like other call methods (d3.select().call and native function.call)? Borrowing from d3 and condensing, seems like it would just be:

return exports.apiMethodRegistry[name].apply(null, [].slice.call(arguments, 1));

or, perhaps marginally faster if we don't care about this being the method name:

return exports.apiMethodRegistry[name].apply([].slice.call(arguments));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was going to do until I saw this block (called from updatemenus and silders) where the argument list is built as an array.

I think I would prefer keeping this pattern. I could rename Registry.call -> Registry.apply to avoid confusion on the call signature if you prefer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, that's the one place you use Registry.call without brackets, and it would be awkward to try and fit that into the call pattern. It's a shame that this one call adds cruft to the hundred other ones though. What about creating both Registry.call and Registry.apply methods? Or, since at least for now that's the only place we'd use Registry.apply, just inlining it there:

return Registry.apiMethodRegistry[method].apply(null, allArgs).catch(...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 705aed6 🚀

console.log(circularDeps.join('\n'));
logs.push('some new circular dependencies were added to src/');
logs.push('some circular dependencies were found to src/');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found in src/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good 👁️ done in d27b3ba

@alexcjohnson
Copy link
Collaborator

@etpinard this is brilliant. Beautiful. Heroic even. Amazing that even with the extra machinery it's a net decrease in LOC.

Just a few comments, the only substantive one being the Registry.call signature.

Love it. End of an era.

- move all spec under top-level describe(),
  so that they can easily reuse the 'unregister' wrapper,
- fixup spec naming
... everywhere except for plots/command.js where
    and fn.apply would work better, for now just
    grab api method for registry cache.
... and 'enforce' Registry in components/modebar/
    (like for all components really).
@etpinard
Copy link
Contributor Author

etpinard commented Mar 2, 2018

A couple more things I want to add:

  • first, I'll reference https://github.com/plotly/plotly-webpack/issues/2#issuecomment-333256054 where the first observations on browser-pack-flat +/- circular dependencies were made,
  • second, errorbars and grid components are now totally optional and could be taken out of the core bundle in v2.
  • lastly, now that we're registering plot api methods, we could make some of them optional in v2 (i.e. move them out of src/core.js). If we choose to do, we'll just have to make sure the components that call api methods register them beforehand (similar to how trace modules register subplot modules).

@@ -196,7 +196,7 @@ function toImage(gd, opts) {
}

return new Promise(function(resolve, reject) {
Registry.call('plot', [clonedGd, data, layoutImage, configImage])
plotApi.plot(clonedGd, data, layoutImage, configImage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious, why did you choose not to use Registry here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plotly.toImage shouldn't be in a circular dep with the other Plotly methods. Plotly.toImage should call Plotly.plot but never the other way around.

Ideally, I think the api method registry should only be used in components/ and in the subplot modules (for interaction updates). Plotly methods need to call component methods (e.g .draw) and some components need to call Plotly methods (e.g. updatemenus / mode bar button click) - this is circular, hence the need for an api method registry.

@alexcjohnson
Copy link
Collaborator

💃 💃 💃

@etpinard etpinard merged commit 13eb982 into master Mar 2, 2018
@etpinard etpinard deleted the zero-circular-dep branch March 2, 2018 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

De-circularize plotly.js require statements
3 participants