amd-dependency not honored

Topics: General
Mar 1, 2014 at 11:13 AM
Edited Mar 1, 2014 at 11:14 AM
I'm compiling with the amd flag and use RequireJS for module loading in the browser. With 0.9.5, when I had a sourcefile containing just some references and amd-dependencies, it would generate a valid AMD external module with the expected dependencies:

test.ts

/// <reference path="libs/jasmine.d.ts" />
/// <reference path="libs/angular-mocks.d.ts" />
/// <amd-dependency path="angular-mocks"/>
/// <amd-dependency path="foo/bar"/>

// my code here
The fact that foo/bar is loaded at runtime means that an Angular module is defined which I use in my code. I know, side-effecting dependencies don't seem like a great idea, but structuring an Angular app seems to push this way, since Angular has its own module system and injection as well.

However, with 0.9.7, the dependency is no longer added in the generated output. In fact it is isn't even compiled as an external AMD-module anymore. Probably because it is a test-file, which doesn't export anything?

I thought that may be adding an import/require combo restored the old behavior. So I introduced a test-common module where I could put my shared testing type references:

test-common.ts

/// <reference path="libs/jasmine.d.ts" />
/// <reference path="libs/angular-mocks.d.ts" />
/// <amd-dependency path="angular-mocks"/>
export = undefined 
// Only using this for the shared references/amd-dependency for now. Might add some useful common test code here later
and imported this in my original file:

test.ts

/// <amd-dependency path="foo/bar"/>

import testcommon = require('test-common');

// my code here
However, this doesn't work either since 0.9.7 believes I'm not using the 'test-common' import in test.ts and it is pruned. Which is sort-of true, but I do depend on it for the shared references and amd-dependency that it brings in. So I had to resort to (yuck) 'using' the import:

test.ts

/// <amd-dependency path="foo/bar"/>

// If no import is done, the amd-dependencies aren't added by tsc either.
import testcommon = require('test-common');
// An unused import is removed by tsc, hence 'use it'...
console.log(testcommon);
My question is three-fold:
  1. Is it by design that tsc no longer emits an external module when only amd-dependencies are present?
  2. Why would tsc believe that importing test-common (without using it in the code) is a no-op, since it clearly contributes an amd-dependency which is vital at runtime?
  3. I'm sure I'm horribly over-complicating this and there might be a much easier way to do what I want. Please do enlighten me.
(although, I arrived at this setup because I have my test files in a separate directory from the application sources and want to test them with Jasmine in the browser using AMD. Using the amd-dependency seemed the best way to have tsc ignore the dependency at compile-time while still retaining it at runtime.Ssince using import/require for 'foo/bar' would lead to some strange path-issues, and I still wouldn't necessarily use the import directly)

Thanks in advance!

Sander
Mar 4, 2014 at 5:01 PM
Edited Mar 4, 2014 at 5:04 PM
I have what I think is the same problem and found the same workaround also on 1.0 RC.

I just got started on a d.ts file for DefinitelyTyped for Knockout.punches. For now, the d.ts file is:
declare module 'knockout.punches' {
}

interface KnockoutStatic {
    punches: any
}
When I use Knockout and this new Knockout.punches definition in my Durandal module for my page, unless I call into the variable returned from the require statement, ko.punches doesn't exist because "knockout.punches" won't exist in the generated JavaScript. Specifically:

If my page.ts looks like this:
import app = require('durandal/app');
import ko = require('knockout');
import kop = require('knockout.punches');

ko.punches.enableAll();

return {name: "MyApp"}
Then my page.js looks like this:
define(["require", "exports", 'knockout'], function(require, exports, ko) {   //knockout.punches is missing...
    ko.punches.enableAll();   //this throws an error.

    return {name: "MyApp"};
});
If my page.ts looks like this:
import app = require('durandal/app');
import ko = require('knockout');
import kop = require('knockout.punches');
kop;  //does no useful work.

ko.punches.enableAll();

return {name: "MyApp"}
Then the generated JavaScript looks like this and everything works.
define(["require", "exports", 'knockout','knockout.punches'], function(require, exports, ko, kop) {
    kop;
    ko.punches.enableAll();   //this works now.

    return {name: "MyApp"};
});
Is there any way to say "really really require this" without having to issue a call against something?
Mar 17, 2014 at 11:30 PM
+1

This behavior is very strange. I can understand the compiler's desire to keep dependencies to a minimum but it makes for a very weird debugging experience. Can the compiler throw a warning or something?
May 19, 2014 at 10:20 PM
I had a similar issue and I found the reason why compiler removes unused modules: http://www.typescriptlang.org/Handbook#modules-optional-module-loading-and-other-advanced-loading-scenarios.

I think your workaround is acceptable:
import kop = require('knockout.punches'); kop;