Resolves fail when using Rollup and multiple classes are included in the build.
How to Reproduce
In order to reproduce the problem, grab the repo ui-router-ng-resolve-bug and build the project using aot by running
git clone https://github.com/cloudmark/ui-router-ng-resolve-bug
npm install
npm run build.prod.rollup.aot
Serve dist/prod
by running python SimpleHttpServer:
python -m SimpleHTTPServer 8000
Goto http://localhost:8000 and navigate to middle tab Details
. Open the console for the full stack trace, you should see No provider for Transition!.
This works if you compile with aot only (systemjs-builder)
npm run build.prod.aot
or whilst developing by using the dev server
npm run start.deving
Note that compress and mangle have been switched off so that the code is more readable. See tools/tasks/seed/minify.bundles.ts
Investigation
Looking at the generated code the problem originates from the getDependencies
method in the ResolveContext
class.
ResolveContext.prototype.getDependencies = function(resolvable) {
var _this = this;
var node = this.findNode(resolvable);
var subPath = PathFactory.subPath(this._path, function(x) {
return x === node
}) || this._path;
var availableResolvables = subPath.reduce(function(acc, node) {
return acc.concat(node.resolvables)
}, []).filter(function(res) {
return res !== resolvable
});
var getDependency = function(token) {
var matching = availableResolvables.filter(function(r) {
return r.token === token
});
if (matching.length)
return tail(matching);
var fromInjector = _this.injector().getNative(token);
if (!fromInjector) {
throw new Error("Could not find Dependency Injection token: " + stringify$3(token))
}
return new Resolvable(token,function() {
return fromInjector
}
,[],fromInjector)
};
return resolvable.deps.map(getDependency)
}
specifically the matching
function.
var matching = availableResolvables.filter(function(r) {
return r.token === token
});
Even though the transition
is present, r.token
and token
never match since their reference is not the same.
Looking at the generated code one can in fact find two classes for Transition: Transition$1
and Transition
.
var Transition$1 = function() {
function Transition(fromPath, targetState$$1, router) {
var _this = this;
this._deferred = coreservices_1.services.$q.defer();
this.promise = this._deferred.promise;
this._registeredHooks = {};
this.isActive = function() {
return _this === _this._options.current()
}
;
this.router = router;
this._targetState = targetState$$1;
if (!targetState$$1.valid()) {
throw new Error(targetState$$1.error())
}
this._options = common_1$6.extend({
and
var Transition = function() {
function Transition(fromPath, targetState, router) {
var _this = this;
this._deferred = services.$q.defer();
this.promise = this._deferred.promise;
this._registeredHooks = {};
this.isActive = function() {
return _this === _this._options.current()
}
;
this.router = router;
this._targetState = targetState;
if (!targetState.valid()) {
throw new Error(targetState.error())
}
this._options = extend({
current: val(this)
}, targetState.options());
Since these tokens were create from two different Transition classes the reference match r.token === token
fails and hence the error: No provider for Transition!
.
So how are these two transition classes created?
The Transition
class is present in ui-router-core. UI-router-core and ui-router-ng2 both have two module builds: cjs and esm as follows:
UI-Router-Core:
- cjs build: lib
folder
- esm build: lib-esm
folder
UI-Router-NG2
- cjs build:
_bundles/ui-router-ng2.js
- esm build:
lib
folder
When the angular compile is running aot the lib
folder from ui-router-core is used. You can verify this by running the following command:
node_modules/.bin/ngc -p tsconfig-aot.json
If you open aot/src/client/app/home/home.module.ngfactory.ts
you can see the import
import * as import18 from 'ui-router-core/lib/transition/transition';
I cannot seem to find a way how to instruct tsc
to obey the module
and jsnext:module
properties in package.json.
Rollup on the other hand obeys the module
and jsnext:module
and hence will use lib-esm
folder.
This is exactly why we end up with two Transition
classes in the final build. One is originating from the lib
(cjs) folder and the other is from lib-esm
being used by rollup.