forked from emberjs/ember.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathengine-instance.js
225 lines (174 loc) · 6.12 KB
/
engine-instance.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/**
@module ember
@submodule ember-application
*/
import EmberObject from 'ember-runtime/system/object';
import EmberError from 'ember-metal/error';
import { Registry } from 'container';
import ContainerProxy from 'ember-runtime/mixins/container_proxy';
import RegistryProxy from 'ember-runtime/mixins/registry_proxy';
import { privatize as P } from 'container';
import { getEngineParent, setEngineParent } from './engine-parent';
import { assert } from 'ember-metal/debug';
import run from 'ember-metal/run_loop';
import RSVP from 'ember-runtime/ext/rsvp';
import { guidFor } from 'ember-metal/utils';
/**
The `EngineInstance` encapsulates all of the stateful aspects of a
running `Engine`.
@public
@class Ember.EngineInstance
@extends Ember.Object
@uses RegistryProxyMixin
@uses ContainerProxyMixin
*/
const EngineInstance = EmberObject.extend(RegistryProxy, ContainerProxy, {
/**
The base `Engine` for which this is an instance.
@property {Ember.Engine} engine
@private
*/
base: null,
init() {
this._super(...arguments);
guidFor(this);
let base = this.base;
if (!base) {
base = this.application;
this.base = base;
}
// Create a per-instance registry that will use the application's registry
// as a fallback for resolving registrations.
let registry = this.__registry__ = new Registry({
fallback: base.__registry__
});
// Create a per-instance container from the instance's registry
this.__container__ = registry.container({ owner: this });
this._booted = false;
},
/**
Initialize the `Ember.EngineInstance` and return a promise that resolves
with the instance itself when the boot process is complete.
The primary task here is to run any registered instance initializers.
See the documentation on `BootOptions` for the options it takes.
@private
@method boot
@param options {Object}
@return {Promise<Ember.EngineInstance,Error>}
*/
boot(options) {
if (this._bootPromise) { return this._bootPromise; }
this._bootPromise = new RSVP.Promise(resolve => resolve(this._bootSync(options)));
return this._bootPromise;
},
/**
Unfortunately, a lot of existing code assumes booting an instance is
synchronous – specifically, a lot of tests assume the last call to
`app.advanceReadiness()` or `app.reset()` will result in a new instance
being fully-booted when the current runloop completes.
We would like new code (like the `visit` API) to stop making this
assumption, so we created the asynchronous version above that returns a
promise. But until we have migrated all the code, we would have to expose
this method for use *internally* in places where we need to boot an instance
synchronously.
@private
*/
_bootSync(options) {
if (this._booted) { return this; }
assert('An engine instance\'s parent must be set via `setEngineParent(engine, parent)` prior to calling `engine.boot()`.', getEngineParent(this));
this.cloneParentDependencies();
this.setupRegistry(options);
this.base.runInstanceInitializers(this);
this._booted = true;
return this;
},
setupRegistry(options = this.__container__.lookup('-environment:main')) {
this.constructor.setupRegistry(this.__registry__, options);
},
/**
Unregister a factory.
Overrides `RegistryProxy#unregister` in order to clear any cached instances
of the unregistered factory.
@public
@method unregister
@param {String} fullName
*/
unregister(fullName) {
this.__container__.reset(fullName);
this._super(...arguments);
},
/**
@private
*/
willDestroy() {
this._super(...arguments);
run(this.__container__, 'destroy');
},
/**
Build a new `Ember.EngineInstance` that's a child of this instance.
Engines must be registered by name with their parent engine
(or application).
@private
@method buildChildEngineInstance
@param name {String} the registered name of the engine.
@param options {Object} options provided to the engine instance.
@return {Ember.EngineInstance,Error}
*/
buildChildEngineInstance(name, options = {}) {
let Engine = this.lookup(`engine:${name}`);
if (!Engine) {
throw new EmberError(`You attempted to mount the engine '${name}', but it is not registered with its parent.`);
}
let engineInstance = Engine.buildInstance(options);
setEngineParent(engineInstance, this);
return engineInstance;
},
/**
Clone dependencies shared between an engine instance and its parent.
@private
@method cloneParentDependencies
*/
cloneParentDependencies() {
let parent = getEngineParent(this);
let registrations = [
'route:basic',
'event_dispatcher:main',
'service:-routing',
'service:-glimmer-environment'
];
registrations.forEach(key => this.register(key, parent.resolveRegistration(key)));
let env = parent.lookup('-environment:main');
this.register('-environment:main', env, { instantiate: false });
let singletons = [
'router:main',
P`-bucket-cache:main`,
'-view-registry:main',
`renderer:-${env.isInteractive ? 'dom' : 'inert'}`
];
singletons.forEach(key => this.register(key, parent.lookup(key), { instantiate: false }));
this.inject('view', '_environment', '-environment:main');
this.inject('route', '_environment', '-environment:main');
}
});
EngineInstance.reopenClass({
/**
@private
@method setupRegistry
@param {Registry} registry
@param {BootOptions} options
*/
setupRegistry(registry, options) {
// when no options/environment is present, do nothing
if (!options) { return; }
registry.injection('view', '_environment', '-environment:main');
registry.injection('route', '_environment', '-environment:main');
if (options.isInteractive) {
registry.injection('view', 'renderer', 'renderer:-dom');
registry.injection('component', 'renderer', 'renderer:-dom');
} else {
registry.injection('view', 'renderer', 'renderer:-inert');
registry.injection('component', 'renderer', 'renderer:-inert');
}
}
});
export default EngineInstance;