uksz uksz - 3 months ago 31
TypeScript Question

How to prepare release version with SystemJS and Gulp?

I use SystemJS in my Angular2 project. I use tsconfig file for TypeScript. I want to use gulp to concat and minify my code for production version. I am having an issues with concating the code: each time I try to concat files I get either 'angular' not defined or 'system' not defined. I tried to modify the order that I try to load my files from node modules, however I did not succeeded.

I was wondering if any of you had this issues, and found an answer to it?

Here is my gulp file:

var gulp = require('gulp'),
.....


var paths = {
dist: 'dist',
vendor: {
js: [
'node_modules/systemjs/dist/system.src.js',
'node_modules/angular2/bundles/angular2.dev.js',
'node_modules/angular2/bundles/angular2-polyfills.js',
'node_modules/angular2/bundles/router.dev.js'
...
],
css: []
},
app: {
templates: [
'app/**/*.html',
'!node_modules/*.html'
],
scripts: [
'app/**/*.ts',
'app/config.ts',
'app/app.ts'
]
}
};

var tsProject = ts.createProject('tsconfig.json', {
out: 'Whatever.js'
});

gulp.task('dev:build:templates', function () {
return gulp.src(paths.app.templates)
.pipe(ngHtml2Js({
moduleName: 'Whatever',
declareModule: false
}))
.pipe(concat("Whatever.tpls.min.js"))
.pipe(gulp.dest(paths.dist));
});
gulp.task('prod:build:templates', function () {
return gulp.src(paths.app.templates)
.pipe(minifyHtml({
empty: true,
spare: true,
quotes: true
}))
.pipe(ngHtml2Js({
moduleName: 'whatever',
declareModule: false
}))
.pipe(concat(paths.appName + ".tpls.min.js"))
.pipe(uglify())
.pipe(gulp.dest(paths.dist));
});

gulp.task('dev:build:scripts', function () {
var tsResult = tsProject.src()
.pipe(sourcemaps.init())
.pipe(ts(tsProject));

return tsResult.js
.pipe(sourcemaps.write({
sourceRoot: '/app'
}))
.pipe(concat('whatever.js'))
.pipe(gulp.dest(paths.dist));
});

gulp.task('dev:build:styles', function () {
return gulp.src(paths.app.styles)
.pipe(sass())
.pipe(gulp.dest(paths.dist + '/css'));
});
gulp.task('dev:build:vendor', function () {
return gulp.src(paths.vendor.js)
.pipe(concat('vendor.min.js'))
.pipe(gulp.dest(paths.dist))
});

gulp.task('dev:build', [
'dev:build:vendor',
'dev:build:templates',
'dev:build:scripts',
'dev:build:styles',
], function () {
});


This is how I load my files:

<script src="vendor.min.js"></script>
<script src="Whatever.js"></script>
<script src="Whatever.tpls.min.js"></script>


And here are the erors that I am getting:

Uncaught TypeError: Unexpected anonymous System.register call.(anonymous function) @ vendor.min.js:2680load.metadata.format @ vendor.min.js:3220oldModule @ vendor.min.js:3749(anonymous function) @ vendor.min.js:2411SystemJSLoader.register @ vendor.min.js:2636(anonymous function) @ Whatever.js:2
Whatever.tpls.min.js:1 Uncaught ReferenceError: angular is not defined

Answer

You will get " Unexpected anonymous System.register call" because the references are not being loaded in the correct order. I use JSPM to properly build my angular app for production. There are 4 parts to the process.

Part 1: Compile your typescript files

var ts = require("gulp-typescript");
var tsProject = ts.createProject("./App/tsconfig.json");
gulp.task("compile:ts", function () {
    var tsResult = tsProject.src()
        .pipe(ts(tsProject));
    tsResult.js.pipe(gulp.dest("./wwwroot/app"));

});

Part 2: Configure config.js (to tell JSPM how to bundle your app):

System.config({
  baseURL: "/",
  defaultJSExtensions: true,
  paths: {
    "npm:*": "jspm_packages/npm/*",
    "github:*": "jspm_packages/github/*",
    "node_modules*": "node_modules/*"
  },
  map: {
    'app': 'app',
    'rxjs': 'node_modules/rxjs',
    '@angular': 'node_modules/@angular'
  },
  packages: {
    'app': { main: 'bootDesktop.js', defaultExtension: 'js' },
    'rxjs': { defaultExtension: 'js' },
    '@angular/common': { main: 'index.js', defaultExtension: 'js' },
    '@angular/compiler': { main: 'index.js', defaultExtension: 'js' },
    '@angular/core': { main: 'index.js', defaultExtension: 'js' },
    '@angular/http': { main: 'index.js', defaultExtension: 'js' },
    '@angular/platform-browser': { main: 'index.js', defaultExtension: 'js' },
    '@angular/platform-browser-dynamic': { main: 'index.js', defaultExtension: 'js' },
    '@angular/router': { main: 'index.js', defaultExtension: 'js' },
    '@angular/router-deprecated': { main: 'index.js', defaultExtension: 'js' },
    '@angular/testing': { main: 'index.js', defaultExtension: 'js' },
    '@angular/upgrade': { main: 'index.js', defaultExtension: 'js' }
  }


});

Part 3: Use gulp-jspm-build to bundle up your app (I was previously using gulp-jspm but it was causing errors, so I switched to gulp-jspm-build):

var jspm = require('gulp-jspm-build');
gulp.task("jspm_bundle", function () {
return jspm({
    bundleOptions: {
        minify: true,
        mangle: false
    },
    bundleSfx: true,
    bundles: [
        { src: './wwwroot/app/appBoot.js', dst: 'boot.bundle.min.js' }
    ]
})
.pipe(gulp.dest('./wwwroot/js-temp'));


});
//this will create a file called boot.bundle.min.js
//note I have set jspm to create a self-executing bundle
//I put mangle to false because mangling was causing errors 

4: Now concat all your already minified assets:

gulp.task("min:js", ["jspm_bundle"], function () {
    //this only concats boot.bundle.min.js
    //and dependencies.min.js which has already been minified such as es6-shim.js
    var files = [
        "./wwwroot/js-temp/dependencies.min.js",
        "./wwwroot/js-temp/boot.bundle.min.js"
    ];

    return gulp.src(files)
        .pipe(concat("boot.bundle.min.js"))
        .pipe(gulp.dest("./wwwroot/js"));

});

Finally, put one nice tidy script reference into your index.html:

<script src="~/js/boot.bundle.min.js"> </script>
One of the nice features of this approach is that your bundled app will only contain the assets that are actually referenced in you import statements (jspm won't bundle it if you don't need it).

UPDATE: Revised config.js to conform to a Angular 2.0-rc.0 appp

UPDATE 2: tsconfig.json looks like this:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "declaration": false,
    "noLib": false,
    "target": "es5",
    "outDir": "wwwroot/app/"
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}