Compile to Single File vs Multiple Files with Modules

Topics: General
May 11, 2013 at 2:19 PM
Hi,

This is a question related to project organization and build practices.

Currently I have tried 2 ways of creating web apps with typescript. For both of them, I have used a specific folder for all my "app code", like "~/App", and did the following ways:
  1. Compile each .ts file to correspondent .js file, and use Bundles to merge them together adding "~/App/*.js" to the bundle. I saw that this approach allows me to specify certain files first in the bundle, so the startup code can run in a specific order while executing the script. The problem with this approach is that I can accidentally leave an outdated .js file that wasn't added to the project for some reason, and this will be included into the bundle. This already cost me some debug time to find out why some event was running and was because a TS file I deleted left a corresponding .JS in the folder but wasn't in the project. Once you notice this its not hard to avoid though.
  2. Compile all the .ts marked with TypeScriptCompile to a single file using the BeforeBuild action, and add that specific file to the bundle. This approach has the benefit the project defining which file will be included in the build, since you can remove a file from the bundle by setting the BuildAction to None. The problem I have noticed with this approach is that I cannot determine the order of the build, so I avoid any executing code into the files, and have only one "Startup.ts" that runs on "document ready" function calling the general startup code in the right order.
For both approaches I have used bundles. Still cannot see any benefits in modules for applications, as I'll need to load all the code at some point anyway, its better to load everything minified at once.

A third option would be to have a custom build tool, but I don't think its worth doing that for now.

Seems to me that the latter will be more aligned to some "typescript project" that is supposed to come up when

I'd like to know how people are doing this in their projects, and if there is any point in using modules instead of bundling everything together.

Thanks,
May 12, 2013 at 10:23 AM
If you are using Node.js my builder tool might help for now though it is in rapid development (I am testing it and some things are changing due to ease of use and because of 0.9 release) - project isn't published yet and no documentation is available for now:
https://github.com/jzvelc/typescript-builder

Copy "node-mvc" example from examples folder and run:
npm install
node app.js
May 12, 2013 at 4:32 PM
Been struggling entire weekend with trying to compile to a single file properly.
Have tried every combination including the undocumented amd-dependency option, import module, reference paths, etc.
Every option failed in my scenario.

Here's how I finally solved how to compile a single external module file from many separate files with classes.
Files are separated during development (no module inside the file, just a class not marked external).

When compiling, this script doing these steps:
  1. run "cat" to combine all files into 1, pipe "sed" to remove all reference paths
  2. run "sed" to change "class xxx" to "export class xxx"
  3. run tsc to compile
  4. other post processing such as closure compiler etc.
The reason for this is, I want to have a single TS and JS file which is easily distributed for other devs to use.
I hope I didn't miss something obvious :) This would be great if it was supported by default.

BR,
Victor
May 12, 2013 at 5:16 PM
Logotype this builder I have provided is doing just that. By specifying input folder or separate files and one output file (.ts or .js) it generates one single merged file.

.ts:
It reads all .ts files, fixes reference and import paths and writes all the code into one concatenated file (type check is available on every input file before concat).

.js:
It does same as .ts and after concatenation compiles the .ts file to .js.

I have following build structure for my temper project:
/builds: core.json, temper.json, utility.json (all build scripts)
/cache: cached builds
/lib: compiled js files
/src: concatenated core.ts and utility.ts files
/src/core: source core .ts files
/src/utility: source utility .ts files
/tsb.json (build script which is linked to temper.json, usually executes /lib/...js file after compilation when needed)

temper.json:
links: "builds/utility.json", "builds/core.json"
input: "src/*.ts" (all concatenated files inside src)
output: "lib"

core.json:
input: "src/core" (all files recursively in folder)
output: "src/core.ts"

utility.json
input: "src/utility" (all files recursively in folder)
output: "src/utility.ts"

Output:
/lib/core.js
/lib/core.d.ts
/lib/utlity.js
/lib/utility.d.ts

Note that core.ts is dependent on utility.ts and it imports concatenated /src/utility.ts file to access its classes.

Take a look of this example and you will get the idea:
https://github.com/jzvelc/typescript-builder/tree/master/examples/node-mvc

Documentation is coming soon and many improvements and new features which will allow your projects to copy .d.ts files into your project fixing its paths (some things may change until I publish it on npm, on git I will create additional branches for further releases).
You mentioned that you are using external modules and step 2 you are adding export prefix (external modules should have that already?).

Though it would be nice that tsc would supported step 1 out of the box. Keep in mind that module.exports support is coming which will allow you to export your classes directly but I think that concatenation should still be there.
May 12, 2013 at 6:44 PM
jzvelc, cool! Will have a look.

My project (including the build script) is located here:
https://github.com/logotype/LeapMotionTS

Regarding step 2, i had to change the "external" when combined, because it caused some errors when the files were separated.

Thanks!!
May 12, 2013 at 7:46 PM
Edited May 12, 2013 at 7:47 PM
nvivo wrote:
Still cannot see any benefits in modules for applications, as I'll need to load all the code at some point anyway, its better to load everything minified at once.
Few words on why do I use AMDs...
  1. Popular libs like jquery could easily be cached by browser in between web pages... those only have to reference same uri (and header...) such as:
    //ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
    Not mentioning that such CDN services are usually much faster than other options.
  2. Load on demand - which is essential to bigger, scalable, "really modular" webapps.
    Don't take me wrong here. With AMD you still would prefer to bundle some of your modules to lower request fragmentation.
  3. Much cleaner, safer global namespace.
  4. Cleaner dependency management.
PS. RequireJS supports jQuery AMD and jQuery is AMDable... knockout as well. Thats a pity current declarations for those libs do not deal with AMD. (I know it is not hard to extend those... I am doing that myself).
May 12, 2013 at 9:01 PM
jzvelc,

I looked over grunt for this, but for now I was just using msbuild pre/post build tasks as they fit what I need.

Seems you got a simplified grunt specific for typescript, is that right? I'll take a look.


arek_bal,

I understand that for libraries that is great, but as I said, I cannot see any benefits for applications, like a commercial app or a single page app website. I guess I'm still learning what they are meant for.
May 12, 2013 at 11:31 PM
Yes actually it is similar to grunt but it supports many features specific to typescript and many will be added. The best thing of all is that
compile process can be made totally transparent by using code like this:
require('typescript-builder').build('./tsb.json', process.argv, function (err) {
    require('./lib/somefile').somefunction(process.argv.length, process.argv);
});
This way every time you run your application with following command only the parts that were changed since last build will be automatically recompiled:
node app.js
This will even work great when you install it via. npm install if you have following code in package.json:
"scripts": {
    "postinstall": "tsb build --npm"
}
Documentation and publish will be done with 0.9.0 stable release (some things will change).