4
votes

I'm trying to create a single AMD library (module) file which is build out of many small files (TypeScript files compiled to JavaScript-AMD modules).

The problem is:

  1. I have cyclic dependencies between classes. (Parent needs to know child and child needs to know parent)
  2. I want to get rid of all the define() and require() calls, except for the main define for "myLibrary"

The main reasons to create a single file:

  1. Load times (requirejs should not be used to reference all the small files)
  2. Cyclic dependencies that can't(?) be resovled by AMD modules

My main questions is:

  • Is there a tool to do this? r.js (grunt-contrib-requirejs) failed me by leaving all the interior calls to define() inside.

My current project layout is similar to the following example:

root
|-- myLibrary
|   |-- packageA
|   |   |-- ClassA1.ts
|   |   +-- ClassA2.ts
|   |-- packageB
|   |   |-- ClassB1.ts
|   |   +-- ClassB2.ts
|   |-- packageA.ts
|   +-- packageB.ts
+-- myLibrary.ts

The code of myLibrary.ts is simply:

export import packageA = require("./packageA");
export import packageB = require("./packageB");

Which simply compiles to an AMD module like this:

define(["require", "exports", "./myLibrary/packageA", "./myLibrary/packageB"], function (require, exports, packageA, packageB) {
    exports.packageA = packageA;
    exports.packageB = packageB;
});

The package files are structured in the same manner. For example packageA.ts looks like this:

// Typescript source:
export * from "./packageA/ClassA1";
export * from "./packageA/ClassA2";

// Compiled as Javascript:
define(["require", "exports", "./packageA/ClassA1", "./packageA/ClassA2"], function (require, exports, ClassA1_1, ClassA2_1) {
    function __export(m) {
        for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
    }
    __export(ClassA1_1);
    __export(ClassA1_2);
});

What is currently get:

define("myLibrary/packageA/ClassA1",["require", "exports"], function(require, exports) {
    var MyClassA1 = (function() {
        // The content of the class ...
    })();
    exports.MyClassA1 = MyClassA1;
});
define("myLibrary/packageA/ClassA2", ...

// The other stuff ...    

define(["require", "exports", "./myLibrary/packageA", "./myLibrary/packageB"], function (require, exports, packageA, packageB) {
    exports.packageA = packageA;
    exports.packageB = packageB;
});

What I desire:

define(["require", "exports"], function(require, exports) {
    //... The entire code of packageA without define or require.
    var packageA = ...;
    //...
    export.packageA = packageA;
    export.packageB = packageB;
});
3

3 Answers

2
votes

They already have a module bundling in their roadmap.

Since we're at TypeScript 1.6 at the time of writing, and they say it will be implemented in 1.7, you'll need to wait for vnext, that should be released pretty soon.

1
votes

Currently, you would need to utilise some other sort of AMD optimiser. In particular you can use r.js which is part of the RequireJS project. It will inspect your AMD modules and emit them into different layers.

Currently though, if you configure your TypeScript project to utilise UMD, the r.js optimiser does not recognise properly your UMD modules so the only option is to target AMD for your project.

I think even when some sort of concatenation is added to TypeScript 1.7+, you may still want to use a tool that is more feature rich, like r.js, since it will allow better optimisation and control of an AMD app, as layers and dependencies can often be complex in a complex project.

1
votes

Specifying --outFile in conjunction with --module amd or --module system will concatenate all modules in the compilation into a single output file containing multiple module closures.

See outfile + amd.