Pesquisa de site

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.js para bibliotecas de terceiros
  • criar um arquivo boot.js para 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.html para 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.js

    gulp.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.js

    gulp.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.js

    gulp.task('boot-bundle', function() {
      gulp.src('config.prod.js')
        .pipe(concat('boot.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
     });
    

    O config.prod.js simplesmente contém o seguinte:

     System.import('boot')
        .then(null, console.error.bind(console));
    
  • Atualize o arquivo index.html

    gulp.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.html se 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 -prod ou ng serve -prod agrupam 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
});

Artigos relacionados