I've been developing with Angular for a while but I just started with Angular 2, and after finishing the quickstarter from the Angular 2 tutorial y thought about trying to develop the finished application in a server as in production mode.
I had no experience with SystemJS nor Gulp, so it took me a while, but I finally managed to make it work. Now I have 2 issues:
For a Hello World application, it's really heavy, about 20MB, because I had to download lots of files from Angular 2 libraries. My guess is I don´t really need so many, but although I started with only @angular/core and @angular/platform-browser-dynamic, which are the ones referenced in my application ts files, angular kept throwing error messages in deployment until I included the whole @angular library.
On the other hand, when I run the application I see again many single files being downloaded, which is not what I had in mind. There has to be a way to download only a single (minified) file with all the libraries (a bundle?) but I have no idea how to do that.
Can anyone give me a hand with that?
Here are my main configuration files.
system.config.js
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'app', // 'dist',
'@angular': 'lib/@angular',
'rxjs': 'lib/rxjs'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' }
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic'
];
// Individual files (~300 requests):
function packIndex(pkgName) {
packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
}
// Add package entries for angular packages
ngPackageNames.forEach(packIndex);
var config = {
map: map,
packages: packages
};
System.config(config);
})(this);
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="./css/styles.css">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="lib/shim.min.js"></script>
<script src="lib/zone.js"></script>
<script src="lib/Reflect.js"></script>
<script src="lib/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
gulpfile.js
var gulp = require('gulp');
var ts = require('gulp-typescript');
const babel = require('gulp-babel');
var tsProject = ts.createProject('tsconfig.json');
gulp.task('babel-dp', ['resources', 'babel']);
gulp.task('ts-dp', ['resources', 'ts']);
gulp.task('resources', () => {
gulp.src([
'node_modules/core-js/client/shim.min.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/systemjs/dist/system.src.js'
]).pipe(gulp.dest('web/lib'));
gulp.src([
'node_modules/@angular/**/*',
]).pipe(gulp.dest('web/lib/@angular'));
gulp.src([
'node_modules/rxjs/**/*',
]).pipe(gulp.dest('web/lib/rxjs'));
gulp.src([
'app/**/*.js'
]).pipe(gulp.dest('web/app'));
gulp.src([
'styles.css'
]).pipe(gulp.dest('web/css'));
gulp.src([
'index.html',
'systemjs.config.js',
'favicon.ico'
]).pipe(gulp.dest('web'));
});
gulp.task('babel', () => {
return gulp.src([
'app/**/*.ts'
])
.pipe(babel({presets: ['es2015']}))
.pipe(gulp.dest('web/js'));
});
gulp.task('ts', function(done) {
var tsResult = tsProject.src([
'app/**/*.ts'
])
.pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('web/js'));
});
Let me know if you need any other file. Many thanks!
UPDATE
I just found out that I can use the umd.min.js files inside @angular instead of the single js files. That is something, from 20 to 5MB, but still seems quite a lot, when the minified version of Angular 1 is less than 1MB.
On the other hand, even though I used these umd.min.js files, chrome is still downloading single files when loading the application.
UPDATE 2
I managed to create a single bundle file using systemjs-builder as suggested in the comments. Now I have a single file bundle.app.js with all my application code, and my index.html now looks like this
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="css/styles.css">
<!-- 1. Load libraries -->
<script src="app/bundle.app.js"> </script>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script>
System.import('app').catch(function(err) { console.error(err); });
</script>
</head>
<!-- 2. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
but now chrome can´t find the js imported in html: shim.min.js, zone.js, Reflect.js and system.src.js. I added a line in systemjs.config.js to include node_modules just in case, but I don´t think it's necessary. Anyway, it's not working either. Shouldn´t these imports be included in the bundle already?
Ok, I finally got a working configuration, so I'll copy here all files needed in case somebody finds it helpful.
index.html
<html>
<head>
<title>Angular 2 QuickStart Deploy</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="css/styles.css">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="lib/shim.min.js"></script>
<script src="lib/zone.js"></script>
<script src="lib/Reflect.js"></script>
<script src="lib/system.src.js"></script>
</head>
<!-- 2. Display the application -->
<body>
<my-app>Loading...</my-app>
<!-- application bundle -->
<script src="app/bundle.app.js"></script>
</body>
</html>
gulpfile.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const babel = require('gulp-babel');
const Builder = require('systemjs-builder');
// systemjs-builder
const builder = new Builder('.', 'systemjs.config.js');
// typescript transpiler
var tsProject = ts.createProject('tsconfig.json');
gulp.task('babel-dp', ['resources', 'babel']);
gulp.task('ts-dp', ['resources', 'ts']);
gulp.task('bundle:app', () => {
builder.buildStatic('app/*.js', 'web/app/bundle.app.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('error ' + err);
})
})
gulp.task('resources', () => {
gulp.src([
'node_modules/core-js/client/shim.min.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/systemjs/dist/system.src.js'
]).pipe(gulp.dest('web/lib'));
gulp.src([
'styles.css'
]).pipe(gulp.dest('web/css'));
gulp.src([
'index.html',
'favicon.ico'
]).pipe(gulp.dest('web'));
});
gulp.task('babel', () => {
return gulp.src([
'app/**/*.ts'
])
.pipe(babel({presets: ['es2015']}))
.pipe(gulp.dest('web/js'));
});
gulp.task('ts', function(done) {
var tsResult = tsProject.src([
'app/**/*.ts'
])
.pipe(ts(tsProject));
//return tsResult.js.pipe(gulp.dest('web/js'));
return tsResult.js.pipe(gulp.dest('app/js'));
});
The rest of the files have not changed. The main points here for me are the use of systemjs-builder to create the bundle (thanks to @Thorsten for the hint) and the use of the bundle in the index.html.