Best way to structure a library with many exports and co-dependencies?

Topics: General
Nov 6, 2013 at 6:30 PM
Edited Nov 6, 2013 at 6:31 PM
I'm building a fairly large library, and for practical reasons, I have all of the code split into 10 or so files by area of responsibility.

Each file has co-dependencies on classes/functions/vars exported by other files.

The consumer of the full library will need access to potentially all of the exported classes/functions/vars.

I read this explanation, and it did clarify some things for me - for one, I'm pretty sure I should build this library separately from any program that might consume the library.

I'd like the build the library using --out filename.js --module=AMD --declaration so that the full library gets compiled into a single-file library with a declaration file, so that I can build programs that consume this library as a single external component dependency.

At the moment, I am not using module statements in the files. Should I? If so, should (or can) I use the same module-name for each file in the entire library?

Do I have to explicitly import every referenced function/class/var in every individual file?

For reference, can you post a link to a library (with source code) that was built in this way?

Thanks!
Nov 6, 2013 at 10:41 PM
Modules in separate files works just fine and is a good way to go. If you're compiling to a single file and not using export then you don't need import.

My project has hundreds of classes all sharing the same root namespace. Lame example:
// one file
module x.y.z {
    export class Foo {}
}

// another file
module x.y.q {
    export class Bar {
        member: x.y.z.Foo = null;
    }
}
Nov 7, 2013 at 5:20 PM
Edited Nov 7, 2013 at 5:21 PM
My problem is, this is not an application, it's a library divided into individual files for practical purposes.

All of the symbols in these files are top-level exports, e.g. export function xyz() - and there are inter-dependencies between the classes/function/vars across files.

Essentially, it's all one module, but I really want to avoid using the module keyword, since putting all of these functions/classes/vars in a namespace would require me to run around and qualify every reference to every symbol with the name of the namespace, throughout the entire codebase, which would look horrible.

I was hoping I would be able to use top-level exports and generate a declaration-file. Then compile the library into a single file with --module AMD so that the exports won't pollute the global namespace, but so that references between symbols in the library itself would still be top-level symbols.

It appears that's not going to be possible?
Nov 8, 2013 at 3:04 AM
TypeScript does not support --out when using external modules (i.e. compiling for AMD or CommonJS module systems). When using external modules, one source file === one module.

You can use something like r.js to optimize the output into one JavaScript file after compilation (see http://www.requirejs.org/docs/optimization.html )

Multi-source-file external modules are a common request, and we've had many discussions on it, but unfortunately it's not something we're likely to get to for the v1.0 timeframe.
Nov 8, 2013 at 2:15 PM
Yes, I discovered --out and --module don't work together. (you would think there would be an error-message for that, but it just quietly ignores --out.)

I know I can use third-party tools like r.js for all sorts of things...

My first impulse, actually, was to set up a Grunt task and serialize the ts files prior to compiling - a bit to my surprise, that actually worked, and solved nearly all my problems.

At the expense of losing the source-maps and any ability to debug the damn thing.

Another evening wasted trying to get this damn thing to build, and still no clue how to fix this, short of completely reorganizing the entire codebase and introducing tons of redundant import statements everywhere...

I've been really excited about TypeScript up to this point, but this is a huge shortcoming and it's really, really frustrating to have to give up the sensible structure that any plain JavaScript project could have had, just to work around this shortcoming in the compiler...

I may just go with the Grunt task approach and make do without the source-maps, in the hopes that this will be sorted out in the future... or maybe I will simply let it build with the infinite list of errors, since I do get working source-code out of it... either way, it's a really ugly decision to have to make and decimate an otherwise very elegant project - entirely the wrong note on which to end an otherwise very pleasant experience...

I guess it was too early to get this excited and invested in TS... :-(
Nov 8, 2013 at 2:33 PM
You know what would work?

A simple directive, like /// <include path="..." /> - the compiler would interpret this directive as simply "pasting" another source-file's content at the point where it's included. Dead simple.

That way, I would just write a simple "header" file with a list of include statements, and compile that.

The only real obstruction, as far as I can figure, would be support for source-maps, since you'd have to keep track of which individual lines the included lines of code in the header-file came from and emit the correct file-names/positions.

As explained, simply concatenating my source-files prior to compilation, works - at the expensive of losing my source-maps...

Does that sound like a big or complicated feature?
Nov 8, 2013 at 2:41 PM
I've posted a feature request.
Nov 8, 2013 at 3:27 PM
Edited Nov 8, 2013 at 3:31 PM
Another feature that could solve this problem would be support for incoming source-maps.

Currently we have support for outgoing source-maps only - if the compiler also supported incoming .ts files with accompanying source-maps, and mapped it's own internal source-map against it before outputting it, you would have something very powerful.

You would then be able to generate TypeScript from other source - the absolute simplest example would be a file concatenator that emits a source-map, and I would be very surprised if such a thing doesn't already exist, but other more advanced uses might be DSLs, template engines, etc.

UglifyJS for example has support for both incoming and outgoing source maps.

Thoughts?