-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
[FEATURE ember-module-unification] Initial implementation #15368
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left comments for a few minor items. The testing and feature flag stuff can be improved but the functionality here looks good at a glance. Please do cross-link to the PRs and issues elsewhere.
packages/container/lib/container.js
Outdated
@@ -1,5 +1,5 @@ | |||
/* globals Proxy */ | |||
import { assert } from 'ember-debug'; | |||
import { assert, isFeatureEnabled } from 'ember-debug'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwjblue am I correct in suggesting this should be using ember/features
now? Like so:
import { EMBER_MODULE_UNIFICATION } from 'ember/features';
Hm I don't think we have any docs on this in CONTRIBUTING.md
.
packages/container/lib/container.js
Outdated
// with ember-module-unification, if expandLocalLookup returns something, | ||
// pass it to the resolve without the source | ||
fullName = expandedFullName; | ||
delete options.source; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if here and at the other delete
call further down we should not be mutating the passed-in object? Instead allocate a new one.
packages/container/lib/registry.js
Outdated
return name; | ||
} | ||
|
||
return options && options.source ? `${options.source}:${name}` : name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you wrap the test here in parens just for legibility?
return (options && options.source) ? `${options.source}:${name}` : name;
|
||
let container = registry.container(); | ||
|
||
strictEqual(container.factoryFor('component:my-input', { source: src }).class, PrivateComponent, 'The correct factory was provided'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests are passed assert
as an argument and should use that over the global assertion methods. For example:
QUnit.test('The container can pass a source to factoryFor', function(assert) {
/* ... */
assert.strictEqual(
container.factoryFor('component:my-input', {source: src}).class, PrivateComponent,
'The correct factory was provided'
);
/* ... */
In fact re-using the string 'component:my-input'
instead of re-typing it would clean things up as well, as would using source
instead of src
as the source string variable name (then this can be {source}
).
let container = registry.container(); | ||
|
||
strictEqual(container.factoryFor('component:my-input', { source: src }).class, PrivateComponent, 'The correct factory was provided'); | ||
strictEqual(container.factoryManagerCache[`template:routes/application:component:my-input`].class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of asserting the contents of the private cache, can the cache be make _
private and here you can assert resolve
or the method on the resolver isn't called a second time? or that it is called with a different source
? Test the behavior, not the state of the cache?
7288544
to
1f40d58
Compare
packages/container/lib/container.js
Outdated
// pass it to the resolve without the source | ||
normalizedName = expandedFullName; | ||
options = copy(options, true); | ||
delete options.source; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copying all properties then deleting one is not ideal. It likely causes the object to go into hash table mode which is a slower data structure.
It would be ideal to create an object with only the data we want to pass forward. Kind of silly that we are doing all this work since I think there are no other properties on options
:-p
But please just make an object. Even options = {}
would be better if there are no other options to copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is scenario that will never get hit in practice because it would only be executed if expandedLocalLookup
is implemented and returns something. Are you suggesting that I do options = {};
? I'm concerned that some upstream caller might add an addition property to options
and expect it to be passed to the resolver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iezer Right, options
as an argument to lookup
is only a contract with the lookup
method. We don't document the argument as resolverOptions
where it is implied the object is passed transparently.
A patch that build the object up by iterating Object.keys
but skipping source
would be ok, but really IMO just building the new object (in this case passing an empty object) is fine. Building a whole new object by iterating would be slow, calling the delete
after copying is slow, and this is fairly performance sensitive code. Doing the fast and API-conservative thing is ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I'll make that change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talked with @dgeb, and I think this psuedo code would be correct for us to ensure we get the higher precedence for local/private lookups over global when resolving a pair of things:
`
a:/my-app/widgets/x-mark
b:/my-app/widgets/top-level/x-mark
`
let source = 'b:/my-app/widgets/top-level';
let a = di.lookup('a:x-mark', {source})
let pairedB;
if (a) {
pairedB = di.lookup('b', {source: a})
}
let b;
if (pairedB) {
b = pairedB;
} else {
b = di.lookup('b:x-mark', {source})
if (b) {
let pairedA = di.lookup('a', {source: b})
a = pairedA;
}
}
let paired = [a,b]
return paired;
1f40d58
to
bd5ad81
Compare
@mixonic I'm not sure I follow that example. When you call |
e7cdb3f
to
14b5e36
Compare
* Pass `source` to the resolver's `resolve` method as a second argument * Update the logic for component pairs to ensure local/private resolution of a component/template are only matched with another local/private resolution.
14b5e36
to
5e0a1fd
Compare
I've added the commit prefix and an entry to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all your hard work @iezer. I think you've walled everything off appropriately behind the feature flag, which should allow us to iterate on the implementation.
To support module unification, we must modify the container and registry to pass the source as second parameter to the resolver. This PR introduces a new feature flag called
ember-module-unification
which does just that.I'll follow up with some PRs in the
ember-resolver
andglimmer-resolver
repos, and an end-to-end example of an Ember app using module unification and private components.When the feature flag is on, this change breaks two tests related to
expandLocalLookup
. I'm looking at a way to support the new functionality and make those tests pass. While I'm doing that, I'd appreciate some feedback on the overall design here.EDIT
Related PRs:
@mixonic @rwjblue