Como eu realmente deployar um aplicativo Angular 2 + Typescript + systemjs?
Há um tutorial rápido em angular.io que usa TypeScript & systemjs. Agora que eu consegui colocar esse miniapp para rodar, como eu faria para criar algo que possa ser implantado? Eu não consegui encontrar nenhuma informação sobre isso em lugar nenhum.
Preciso de alguma ferramenta extra, alguma configuração adicional no System.config?
(Eu sei que poderia usar webpack e criar um único bundle.js, mas eu gostaria de usar systemjs, já que ele é usado no tutorial)
Alguém poderia compartilhar seu processo de build com esta configuração (Angular 2, TypeScript, systemjs)
Resposta #1
A coisa principal a entender neste nível é que, usando a seguinte configuração, você não pode concatenar arquivos JS compilados diretamente.
Na configuração do compilador TypeScript:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": false,
"stripInternal": true,
"module": "system",
"moduleResolution": "node",
"noEmitOnError": false,
"rootDir": ".",
"inlineSourceMap": true,
"inlineSources": true,
"target": "es5"
},
"exclude": [
"node_modules"
]
}
No HTML
System.config({
packages: {
app: {
defaultExtension: 'js',
format: 'register'
}
}
});
De fato, esses arquivos JS conterão módulos anônimos. Um módulo anônimo é um arquivo JS que usa System.register mas sem o nome do módulo como primeiro parâmetro. Isso é o que o compilador TypeScript gera por padrão quando o systemjs está configurado como gerenciador de módulos.
Então, para ter todos os seus módulos em um único arquivo JS, você precisa aproveitar a propriedade outFile dentro da configuração do compilador TypeScript.
Você pode usar o seguinte dentro do gulp para fazer isso:
const gulp = require('gulp');
const ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json', {
typescript: require('typescript'),
outFile: 'app.js'
});
gulp.task('tscompile', function () {
var tsResult = gulp.src('./app/**/*.ts')
.pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('./dist'));
});
Isto poderia ser combinado com algum outro processamento:
- para uglificar coisas os arquivos TypeScript compilados
- criar um arquivo
app.js - criar um arquivo
vendor.jspara bibliotecas de terceiros - criar um arquivo
boot.jspara importar o módulo que inicializa a aplicação. Este arquivo deve ser incluído no final da página (quando toda a página estiver carregada). - atualizar o
index.htmlpara levar em conta esses dois arquivos
As seguintes dependências são usadas nas tarefas do gulp:
- gulp-concat
- gulp-html-replace
- gulp-typescript
- gulp-uglify
O seguinte é um exemplo, então pode ser adaptado.
Crie o arquivo
app.min.jsgulp.task('app-bundle', function () { var tsProject = ts.createProject('tsconfig.json', { typescript: require('typescript'), outFile: 'app.js' }); var tsResult = gulp.src('app/**/*.ts') .pipe(ts(tsProject)); return tsResult.js.pipe(concat('app.min.js')) .pipe(uglify()) .pipe(gulp.dest('./dist')); });Crie o arquivo
vendors.min.jsgulp.task('vendor-bundle', function() { gulp.src([ 'node_modules/es6-shim/es6-shim.min.js', 'node_modules/systemjs/dist/system-polyfills.js', 'node_modules/angular2/bundles/angular2-polyfills.js', 'node_modules/systemjs/dist/system.src.js', 'node_modules/rxjs/bundles/Rx.js', 'node_modules/angular2/bundles/angular2.dev.js', 'node_modules/angular2/bundles/http.dev.js' ]) .pipe(concat('vendors.min.js')) .pipe(uglify()) .pipe(gulp.dest('./dist')); });Crie o arquivo
boot.min.jsgulp.task('boot-bundle', function() { gulp.src('config.prod.js') .pipe(concat('boot.min.js')) .pipe(uglify()) .pipe(gulp.dest('./dist')); });O
config.prod.jssimplesmente contém o seguinte:System.import('boot') .then(null, console.error.bind(console));Atualize o arquivo
index.htmlgulp.task('html', function() { gulp.src('index.html') .pipe(htmlreplace({ 'vendor': 'vendors.min.js', 'app': 'app.min.js', 'boot': 'boot.min.js' })) .pipe(gulp.dest('dist')); });O
index.htmlse parece com o seguinte:<html> <head> <!-- Some CSS --> <!-- build:vendor --> <script src="node_modules/es6-shim/es6-shim.min.js"></script> <script src="node_modules/systemjs/dist/system-polyfills.js"></script> <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="node_modules/rxjs/bundles/Rx.js"></script> <script src="node_modules/angular2/bundles/angular2.dev.js"></script> <script src="node_modules/angular2/bundles/http.dev.js"></script> <!-- endbuild --> <!-- build:app --> <script src="config.js"></script> <!-- endbuild --> </head> <body> <my-app>Loading...</my-app> <!-- build:boot --> <!-- endbuild --> </body> </html>
Observe que o System.import('boot'); deve ser feito no final do corpo para aguardar que todos os componentes do seu aplicativo sejam registrados a partir do arquivo app.min.js.
Eu não descrevo aqui a maneira de lidar com a minificação de CSS e HTML.
Resposta #2
Você pode usar o comando angular2-cli build
ng build -prod
https://github.com/angular/angular-cli/wiki/build#bundling
Builds criados com a flag -prod através de
ng build -prodoung serve -prodagrupam todas as dependências em um único arquivo e utilizam técnicas de tree-shaking.
Atualizar
Esta resposta foi enviada quando o angular2 estava no rc4
Eu tentei novamente no angular-cli beta21 e no angular2 ^2.1.0 e está funcionando como esperado
Esta resposta requer inicializar o aplicativo com angular-cli que você pode usar
ng new myApp
Ou em um existente
ng init
Atualização 08/06/2018
Para o Angular 6, a sintaxe é diferente.
ng build --prod --build-optimizer
Verifique a documentação
Resposta #3
Você pode construir um projeto Angular 2 (2.0.0-rc.1) em Typescript usando SystemJS com Gulp e SystemJS-Builder.
Abaixo está uma versão simplificada de como construir, agrupar e minimizar o Tour of Heroes rodando na versão 2.0.0-rc.1 (código completo, exemplo ao vivo).
gulpfile.js
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var concat = require('gulp-concat');
var typescript = require('gulp-typescript');
var systemjsBuilder = require('systemjs-builder');
// Compile TypeScript app to JS
gulp.task('compile:ts', function () {
return gulp
.src([
"src/**/*.ts",
"typings/*.d.ts"
])
.pipe(sourcemaps.init())
.pipe(typescript({
"module": "system",
"moduleResolution": "node",
"outDir": "app",
"target": "ES5"
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('app'));
});
// Generate systemjs-based bundle (app/app.js)
gulp.task('bundle:app', function() {
var builder = new systemjsBuilder('public', './system.config.js');
return builder.buildStatic('app', 'app/app.js');
});
// Copy and bundle dependencies into one file (vendor/vendors.js)
// system.config.js can also bundled for convenience
gulp.task('bundle:vendor', function () {
return gulp.src([
'node_modules/jquery/dist/jquery.min.js',
'node_modules/bootstrap/dist/js/bootstrap.min.js',
'node_modules/es6-shim/es6-shim.min.js',
'node_modules/es6-promise/dist/es6-promise.min.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/systemjs/dist/system-polyfills.js',
'node_modules/systemjs/dist/system.src.js',
])
.pipe(concat('vendors.js'))
.pipe(gulp.dest('vendor'));
});
// Copy dependencies loaded through SystemJS into dir from node_modules
gulp.task('copy:vendor', function () {
gulp.src(['node_modules/rxjs/**/*'])
.pipe(gulp.dest('public/lib/js/rxjs'));
gulp.src(['node_modules/angular2-in-memory-web-api/**/*'])
.pipe(gulp.dest('public/lib/js/angular2-in-memory-web-api'));
return gulp.src(['node_modules/@angular/**/*'])
.pipe(gulp.dest('public/lib/js/@angular'));
});
gulp.task('vendor', ['bundle:vendor', 'copy:vendor']);
gulp.task('app', ['compile:ts', 'bundle:app']);
// Bundle dependencies and app into one file (app.bundle.js)
gulp.task('bundle', ['vendor', 'app'], function () {
return gulp.src([
'app/app.js',
'vendor/vendors.js'
])
.pipe(concat('app.bundle.js'))
.pipe(uglify())
.pipe(gulp.dest('./app'));
});
gulp.task('default', ['bundle']);system.config.js
var map = {
'app': 'app',
'rxjs': 'vendor/rxjs',
'zonejs': 'vendor/zone.js',
'reflect-metadata': 'vendor/reflect-metadata',
'@angular': 'vendor/@angular'
};
var packages = {
'app': { main: 'main', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'zonejs': { main: 'zone', defaultExtension: 'js' },
'reflect-metadata': { main: 'Reflect', defaultExtension: 'js' }
};
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
System.config({
map: map,
packages: packages
});