Como faço para implantar um aplicativo Angular 2 + Typescript + systemjs?

103

Há um tutorial de início rápido em angular.io que usa o texto digitado e o systemjs. Agora que tenho aquele miniapp em execução, como faria para criar algo implantável? Não consegui encontrar nenhuma informação sobre isso.

Preciso de alguma ferramenta extra, alguma configuração adicional em System.config?

(Eu sei que poderia usar o webpack e criar um único bundle.js, mas gostaria de usar o systemjs como é usado no tutorial)

Alguém poderia compartilhar seu processo de construção com esta configuração (Angular 2, TypeScript, systemjs)

Brian G. Bell
fonte
Aqui está minha receita para construir um aplicativo ng2 para implantação usando JSPM: stackoverflow.com/a/34616199/3532945
brando
2
resposta simples ng build -prod stackoverflow.com/a/38421680/5079380
Amr ElAdawy

Respostas:

66

O principal a ser entendido neste nível é que, usando a configuração a seguir, você não pode concatear 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'
    }
  }
});

Na verdade, esses arquivos JS conterão módulos anônimos. Um módulo anônimo é um arquivo JS que usa, System.registermas sem o nome do módulo como primeiro parâmetro. Isso é o que o compilador de texto digitado gera por padrão quando systemjs é configurado como gerenciador de módulo.

Portanto, para ter todos os seus módulos em um único arquivo JS, você precisa aproveitar a outFilepropriedade dentro da configuração do compilador TypeScript.

Você pode usar o seguinte gole interno 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'));
});

Isso pode ser combinado com algum outro processamento:

  • para complicar as coisas, os arquivos TypeScript compilados
  • para criar um app.jsarquivo
  • para criar um vendor.jsarquivo para bibliotecas de terceiros
  • para criar um boot.jsarquivo para importar o módulo que inicializa o aplicativo. Este arquivo deve ser incluído no final da página (quando toda a página estiver carregada).
  • para atualizar o index.htmlpara levar em consideração esses dois arquivos

As seguintes dependências são usadas nas tarefas gulp:

  • gulp-concat
  • gulp-html-replace
  • gulp-typescript
  • engolir em seco

A seguir está um exemplo para que possa ser adaptado.

  • Criar app.min.jsarquivo

    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'));
    });
  • Criar vendors.min.jsarquivo

    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'));
    });
  • Criar boot.min.jsarquivo

    gulp.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 index.htmlarquivo

    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 se index.htmlparece 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 System.import('boot');deve ser feito no final do corpo para aguardar que todos os componentes do seu aplicativo sejam registrados no app.min.jsarquivo.

Não descrevo aqui a maneira de lidar com a minimização de CSS e HTML.

Thierry Templier
fonte
1
você pode criar um repositório github com um exemplo, por favor?
jdelobel
Segui suas instruções e tudo parece bem no que diz respeito ao gole. No entanto, quando executo o aplicativo no navegador, recebo este erro de log do console: "system.src.js: 1625 TypeError não capturado: várias chamadas System.register anônimas no mesmo arquivo de módulo." Alguma ideia do que isso significa e como consertar?
AngularM
@AngularM: você tem o parâmetro outFile? Esta é a chave para o seu erro ;-)
Thierry Templier
Eu o tenho em meu arquivo gulp e tsconfig
AngularM
Você poderia dar uma olhada no projeto do github que enviei? Veja meu comentário acima. Você vê algumas diferenças com o seu código?
Thierry Templier
28

Você pode usar o comando de construção angular2-cli

ng build -prod

https://github.com/angular/angular-cli/wiki/build#bundling

Builds criados com o sinalizador -prod por meio de ng build -prodou ng serve -prodagrupam todas as dependências em um único arquivo e usam técnicas de agitação de árvore .

Atualizar

Esta resposta foi enviada quando angular2 estava em rc4

Eu tentei novamente no angular-cli beta21 e angular2 ^ 2.1.0 e está funcionando como esperado

Esta resposta requer a inicialização do aplicativo com angular-cli que você pode usar

ng new myApp

Ou em um existente

ng init

Atualização 06/08/2018

Para angular 6, a sintaxe é diferente.

ng build --prod --build-optimizer

Verifique a documentação

Amr ElAdawy
fonte
8
Isso requer que seu aplicativo seja estruturado na estrutura opinativa do angular-cli.
Michael Pell
2
@Amr ElAdawy FYI angular-cli mudou para webpack. Esta questão está relacionada ao SystemJS. ng build não funciona para mim.
Shahriar Hasan Sayeed
@ShahriarHasanSayeed Você está se referindo à época em que enviei a resposta ou a vez em que você tentou?
Amr ElAdawy
@AmrElAdawy, você pode adicionar as versões para os módulos onde isso realmente funciona. Angular2 mudou bastante desde julho.
ppovoski
2
É trivial converter o tutorial Tour of Heroes para a versão cli. Apenas gere um novo projeto usando cli e copie os arquivos do tutorial.
Rosdi Kasim
12

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 executando 2.0.0-rc.1 ( fonte completa , 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
});

De aço
fonte
2
Você poderia especificar como executar SystemJs e Gulp?
Jan Drozen
@JanDrozen No mesmo local que seu gulpfile, você pode executar gulp <taskname>onde "taskname" é o nome da tarefa que chama o construtor SystemJS, no meu exemplo acima bundle:app. Nessa tarefa Gulp, você pode usar o módulo npm 'systemjs-builder' para especificar a configuração do sistema e o arquivo de saída.
Steely
@ steely: Obrigado! Funciona como um encanto. Espere pelo destino padrão - o método uglify () está faltando (ou estou faltando alguma coisa). Você pode me explicar esta última parte obscura?
Jan Drozen
@Steely, você poderia guiar como fazer isso com a versão mais recente do angular2?
micronyks
@ Steely.pode fornecer um link final (no github) dos arquivos de compilação do angular2 mais recentes que são necessários para executar o aplicativo de início rápido do angular2?
micronyks
1

Aqui está meu boilerplate MEA2N para Angular 2: https://github.com/simonxca/mean2-boilerplate

É um clichê simples que usa tscpara colocar as coisas juntas. (Na verdade, usa grunt-ts , que em seu núcleo é apenas o tsccomando.) Não é necessário Wekpack etc.

Quer você use ou não o grunhido, a ideia é:

  • escreva seu aplicativo em uma pasta chamada ts/(exemplo public/ts/:)
  • use tscpara espelhar a estrutura de diretório de sua ts/pasta em uma js/pasta e apenas fazer referência aos arquivos da js/pasta em seu index.html.

Para fazer o grunt-ts funcionar (deve haver um comando equivalente para plain tsc, Gulp, etc.), você tem uma propriedade em seu tsconfig.jsonchamado "outDir": "../js"e a referencia em seu gruntfile.jscom:

grunt.initConfig({
  ts: {
    source: {tsconfig: 'app/ts/tsconfig.json'}
  },
  ...
});

Em seguida grunt ts, execute o , o que levará seu aplicativo public/ts/e o espelhará para public/js/.

Lá. Super fácil de entender. Não é a melhor abordagem, mas é uma boa para começar.

Simonxca
fonte
1

A maneira mais fácil que encontrei de agrupar angular rc1 para systemJs é usar gulpe systemjs-builder:

gulp.task('bundle', function () {
    var path = require('path');
    var Builder = require('systemjs-builder');

    var builder = new Builder('/node_modules');

    return builder.bundle([
        '@angular/**/*.js'
        ], 
        'wwwroot/bundle.js', 
        { minify: false, sourceMaps: false })
        .then(function () {
            console.log('Build complete');
        })
        .catch(function (err) {
            console.log('Build error');
            console.log(err);
        });
});

Conforme apontado nos comentários, systemJs atualmente tem problemas ao agrupar componentes usando moduleId: module.id

https://github.com/angular/angular/issues/6131

A recomendação atual (angular 2 rc1) parece ser usar caminhos explícitos, ou seja moduleId: '/app/path/'

Paulo
fonte
Isso parece promissor, mas falha quando uso caminhos relativos para modelos externos no @Componentdecorador. bundle.jstenta resolver caminhos como absolutos, embora sejam relativos, causando erros 404 (consulte stackoverflow.com/questions/37497635/… ). Como você lidou com isso?
BeetleJuice
você está definindo moduleIdpara o caminho relativo?
paul
Não tenho certeza se entendi. Tenho moduleId: module.idem@Component
BeetleJuice
Isso tem as mesmas desvantagens de abrir o caminho completo templateUrle anula o propósito de ter moduleIdem primeiro lugar. Estou tentando usar caminhos relativos conforme recomendado ( angular.io/docs/ts/latest/cookbook/… )
BeetleJuice
você pode ter mais sorte definindo explicitamente o caminho sozinho, por exemplomoduleId: '/app/components/home/'
paul
0

No site Angular.io, na seção Avançado / Implantação, é recomendado que a maneira mais simples de implantar seja 'copiar o ambiente de desenvolvimento para o servidor'.

  1. vá até a seção em: Implementação mais simples possível. Os arquivos finais do projeto são mostrados dentro da seção de código. Observe que ele já configura o código para carregar arquivos de pacote npm da web (em vez da pasta npm_modules local).

  2. certifique-se de que está sendo executado em seu computador local (npm start). Em seguida, na pasta do projeto, copie tudo na subpasta '/ src' para o bucket S3 que você configurou. Você pode usar arrastar e soltar para copiar, durante esse processo, você tem a opção de selecionar a configuração de permissão para os arquivos, certifique-se de torná-los 'legíveis' para 'todos'.

  3. na guia 'Propriedades' do depósito, procure o painel 'Hospedagem de site estático', marque a opção 'Usar este depósito para hospedar o site' e especifique 'index.html' para o documento de índice e o documento de erro.

  4. clique no site estático Endpoint, seu projeto estará em execução!

Joseph Wu
fonte