How to bundle an Angular app for production
2 to 14
(TypeScript) with Angular CLI
OneTime Setup
npm install -g @angular/cli
ng new projectFolder
creates a new application
Bundling Step
ng build
(run in command line when directory isprojectFolder
).flag
prod
bundle for production is now the default (see the Angular documentation to customize it if needed).Compress using Brotli compression the resources using the following command
for i in dist/*/*; do brotli $i; done
bundles are generated by default to projectFolder/dist(/$projectFolder
for v6+)**
Output
Sizes with Angular 14.0.2
with CLI 14.0.2
and option CSS without Angular routing
dist/main.[hash].js
Your application bundled [ size: 118 KB for new Angular CLI application empty, 36 KB compressed].dist/polyfill-[es-version].[hash].bundle.js
the polyfill dependencies (@angular, RxJS...) bundled [ size: 34 KB for new Angular CLI application empty, 11 KB compressed].dist/index.html
entry point of your application.dist/runtime.[hash].bundle.js
webpack loaderdist/style.[hash].bundle.css
the style definitionsdist/assets
resources copied from the Angular CLI assets configuration
Deployment
You can get a preview of your application using the ng serve --prod
command that starts a local HTTP server such that the application with production files is accessible using http://localhost:4200. This is not safe to use for production usage.
For a production usage, you have to deploy all the files from the dist
folder in the HTTP server of your choice.
2.0.1 Final
using Gulp (TypeScript - Target: ES5)
OneTime Setup
npm install
(run in cmd when direcory is projectFolder)
Bundling Steps
npm run bundle
(run in cmd when direcory is projectFolder)bundles are generated to projectFolder / bundles /
Output
bundles/dependencies.bundle.js
[ size: ~ 1 MB (as small as possible) ]- contains rxjs and angular dependencies, not the whole frameworks
bundles/app.bundle.js
[ size: depends on your project, mine is ~ 0.5 MB ]- contains your project
File Structure
- projectFolder / app / (all components, directives, templates, etc)
- projectFolder / gulpfile.js
var gulp = require('gulp'),
tsc = require('gulp-typescript'),
Builder = require('systemjs-builder'),
inlineNg2Template = require('gulp-inline-ng2-template');
gulp.task('bundle', ['bundle-app', 'bundle-dependencies'], function(){});
gulp.task('inline-templates', function () {
return gulp.src('app/**/*.ts')
.pipe(inlineNg2Template({ useRelativePaths: true, indent: 0, removeLineBreaks: true}))
.pipe(tsc({
"target": "ES5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": true,
"noImplicitAny": false
}))
.pipe(gulp.dest('dist/app'));
});
gulp.task('bundle-app', ['inline-templates'], function() {
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('', 'dist-systemjs.config.js');
return builder
.bundle('dist/app/**/* - [@angular/**/*.js] - [rxjs/**/*.js]', 'bundles/app.bundle.js', { minify: true})
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
});
gulp.task('bundle-dependencies', ['inline-templates'], function() {
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('', 'dist-systemjs.config.js');
return builder
.bundle('dist/app/**/*.js - [dist/app/**/*.js]', 'bundles/dependencies.bundle.js', { minify: true})
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
});
- projectFolder / package.json (same as Quickstart guide, just shown devDependencies and npm-scripts required to bundle)
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
***
"gulp": "gulp",
"rimraf": "rimraf",
"bundle": "gulp bundle",
"postbundle": "rimraf dist"
},
"license": "ISC",
"dependencies": {
***
},
"devDependencies": {
"rimraf": "^2.5.2",
"gulp": "^3.9.1",
"gulp-typescript": "2.13.6",
"gulp-inline-ng2-template": "2.0.1",
"systemjs-builder": "^0.15.16"
}
}
- projectFolder / systemjs.config.js (same as Quickstart guide, not available there anymore)
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'app',
'rxjs': 'node_modules/rxjs',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'@angular': 'node_modules/@angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'app/boot.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' }
};
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/forms',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
// add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages
};
// filterSystemConfig - index.asp's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
- projetcFolder / dist-systemjs.config.js (just shown the difference with systemjs.config.json)
var map = {
'app': 'dist/app',
};
- projectFolder / index.html (production) - The order of the script tags is critical. Placing the
dist-systemjs.config.js
tag after the bundle tags would still allow the program to run but the dependency bundle would be ignored and dependencies would be loaded from thenode_modules
folder.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<base href="/"/>
<title>Angular</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<my-app>
loading...
</my-app>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.min.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="dist-systemjs.config.js"></script>
<!-- Project Bundles. Note that these have to be loaded AFTER the systemjs.config script -->
<script src="bundles/dependencies.bundle.js"></script>
<script src="bundles/app.bundle.js"></script>
<script>
System.import('app/boot').catch(function (err) {
console.error(err);
});
</script>
</body>
</html>
- projectFolder / app / boot.ts is where the bootstrap is.
The best I could do yet :)