Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2, Gulp, SystemJS -> Issue with Default Extension

Tags:

I am using Angular2 with SystemJS which worked quite well during development. Now I wanted to deploy my first compiled version using gulp.

To clarify, I use the following systemJS file:

(function(global) {

  // map tells the System loader where to look for things
  var map = {
    'app':                            'app', // 'dist',
    'rxjs':                       'lib/node_modules/rxjs',
    'angular2-in-memory-web-api': 'lib/node_modules/angular2-in-memory-web-api',
    '@angular':                   'lib/node_modules/@angular',
    'ng2-charts/bundles':         'lib/node_modules/ng2-charts/bundles',
    'ng2-charts/components':      'lib/node_modules/ng2-charts/components',
    'ng2-cookies':                'lib/node_modules/ng2-cookies/'
  };

  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                            { format: 'register', defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { 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',
  ];

  // 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 = {
    baseURL: "/",
    defaultJSExtension : true,
    map: map,
    packages: packages
  }

  // filterSystemConfig - index.html's chance to modify config before we register it.
  if (global.filterSystemConfig) { global.filterSystemConfig(config); }

  System.config(config);

})(this);

And the following gulpfile:

const gulp = require('gulp');
const del = require('del');
const typescript = require('gulp-typescript');
const tscConfig = require('./tsconfig.json');
const tsconfig = require('tsconfig-glob');
const sourcemaps = require('gulp-sourcemaps');

// clean the contents of the distribution directory
gulp.task('clean', function () {
  return del('dist/**/*');
});

// TypeScript compile
gulp.task('compile', ['clean'], function () {
  return gulp
    .src(tscConfig.files)
    .pipe(sourcemaps.init())          // <--- sourcemaps
    .pipe(typescript(tscConfig.compilerOptions))
    .pipe(sourcemaps.write('.'))      // <--- sourcemaps
    .pipe(gulp.dest('dist'));
});

// copy dependencies
gulp.task('copy:libs', ['clean'], function() {
  return gulp.src([
      '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/router.dev.js',
      'node_modules/chart.js/dist/Chart.bundle.min.js',
      'node_modules/es6-shim/es6-shim.min.js',
      'node_modules/zone.js/dist/zone.js',
      'node_modules/reflect-metadata/Reflect.js',
      'node_modules/systemjs/dist/system.src.js',
      'TcAdsWebService.js'
    ])
    .pipe(gulp.dest('dist/lib'))
});

gulp.task('copy:modules',['clean'],function() {
  return gulp.src([
    './node_modules/@angular/**/*',
    './node_modules/rxjs/**/**',
    './node_modules/angular2-in-memory-web-api/**/*',
    './node_modules/ng2-charts/**/*',
    './node_modules/ng2-cookies/**/*'
  ],{base:'./'}).pipe(gulp.dest('dist/lib'));
});

gulp.task('copy:pics',['clean'], function () {
  return gulp.src(['pics/**/*'],{base:'./'}).pipe(gulp.dest('dist/css'));
})

gulp.task('copy:css',['clean'],function() {
  return gulp.src(['css/**/*'],{base:'./'}).pipe(gulp.dest('dist/css'));
});

gulp.task('copy:js',['clean'],function() {
  return gulp.src(['js/**/*'],{base:'./'}).pipe(gulp.dest('dist/js'));
});


gulp.task('copy:systemJS',['clean'],function() {
  return gulp.src(['systemjs.config.js']).pipe(gulp.dest('dist'));
});


// copy static assets - i.e. non TypeScript compiled source
gulp.task('copy:assets', ['clean'], function() {
  return gulp.src(['app/**/*', 'index.html', 'styles.css', '!app/**/*.ts'], { base : './' })
    .pipe(gulp.dest('dist'))
});

gulp.task('tsconfig-glob', function () {
  return tsconfig({
    configPath: '.',
    indent: 2
  });
});

gulp.task('build', ['tsconfig-glob','compile', 'copy:pics', 'copy:js', 'copy:css', 'copy:systemJS','copy:modules','copy:libs', 'copy:assets']);
gulp.task('default', ['build']);

After building the angular2 app and loading it in the browser i get the following error in the console:

Unable to load script http://localhost:81/app/app.component

which indicates, that it is missing the .js extension from the compiled files. I am fairly sure, that

'app':                            { format: 'register', defaultExtension: 'js' },

should actually require the compiler to mind the .js extension in the app folder, however it doesn't. This is my first angular2 project and my first gulp compile and I am pretty sure I am missing some basic aspect of it but I can't find it in my code.

like image 408
Marc Engeler Avatar asked Jul 05 '16 12:07

Marc Engeler


1 Answers

Given your setup, you should be using systemjs-builder to bundle your app for production. It accepts your SystemJS configuration so further configuration isn't necessary. It bundles your modules into one file, with options to minify, mangle, etc.

This is doing things the "es6 module" way, making better use of our module loader instead of copy/pasting modules like we would do in a traditional javascript app (es5).

Doing this, we can take the dynamic loading out of the index page and just use a script tag pointed at the bundle, it should drastically speed up load times and minimize what the user has to download to load your page. Copying node_modules shouldn't be needed either.

Given a folder structure:

src
|-- app
|   |-- main.ts
|   |-- index.html
|   |-- bundle.min.js
|-- system.config.js
|-- node_modules
|-- tsconfig.json

You can even do all of this with one gulp task.

Task: (requires yargs):

var SystemBuilder = require('systemjs-builder');
var argv = require('yargs').argv;
var builder = new SystemBuilder();

gulp.task('bundle', function () {
    builder.loadConfig('./system.config.js')
        .then(function () {
            var outputFile = argv.prod ? './src/app/bundle.min.js' : './src/app/bundle.js';
            return builder.buildStatic('app', outputFile, {
                minify: argv.prod,
                mangle: argv.prod,
                rollup: argv.prod
            });
        })
        .then(function () {
            console.log('bundle built successfully!');
        });
});

Run this task:

gulp bundle

or

gulp bundle --prod

index.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <base href="/" />
  <title>Your App</title>

  <link rel="stylesheet" href='styles/bootstrap.min.css' />
  <script src="/bundle.js"></script>
</head>

<body>
  <your-root-component>
  </your-root-component>
</body>

</html>

system.config.js:

(function (global) {

  var config = {
    compiler: "typescript",
    map: {
      'jquery': 'node_modules/jquery/dist',
      'bootstrap': 'node_modules/bootstrap/dist/js',
      "reflect-metadata": "node_modules/reflect-metadata",
      "zone": "node_modules/zone.js/dist",
      "crypto": "node_modules/crypto",
      'rxjs': 'node_modules/rxjs',
      'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
      '@angular': 'node_modules/@angular',
      'moment': 'node_modules/moment',
      'angular2-moment': 'node_modules/angular2-moment',
      'app': 'src/app',
    },
    meta: {
      'node_modules/bootstrap/dist/js/bootstrap.js': {
        format: 'global',
        deps: ['jquery']
      }
    },
    packages: {
      'jquery': { main: 'jquery.js', defaultExtension: 'js' },
      'bootstrap': { main: 'bootstrap.js', defaultExtension: 'js' },
      'zone': { main: 'zone.js', defaultExtension: 'js' },
      'reflect-metadata': { main: 'Reflect.js', defaultExtension: 'js' },
      'crypto': { main: 'sha1.js', defaultExtension: 'js' },
      'rxjs': { main: 'Rx.js', defaultExtension: 'js' },
      'moment':{main: 'moment.js', defaultExtension: 'js'},
      'angular2-moment': { main: 'index.js', defaultExtension: 'js' },
      'app': { main: 'main.js', defaultExtension: 'js' },

      '@angular/common': { main: 'index.js', defaultExtension: 'js' },
      '@angular/compiler': { main: 'index.js', defaultExtension: 'js' },
      '@angular/core': { main: 'index.js', defaultExtension: 'js' },
      '@angular/http': { main: 'index.js', defaultExtension: 'js' },
      '@angular/platform-browser': { main: 'index.js', defaultExtension: 'js' },
      '@angular/platform-browser-dynamic': { main: 'index.js', defaultExtension: 'js' },
      '@angular/router': { main: 'index.js', defaultExtension: 'js' },
      '@angular/testing': { main: 'index.js', defaultExtension: 'js' },
      '@angular/upgrade': { main: 'index.js', defaultExtension: 'js' },
      '@angular/forms': { main: 'index.js', defaultExtension: 'js' },
    }
  }

  System.config(config);

})(this);

With all of that said: I believe your packages section:

  var packages = {
    'app':                            { format: 'register', defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' }
  };

should be:

  var packages = {
    'app':                            { main: 'main.js', defaultExtension: 'js' },
    'rxjs':                       { main: 'Rx.js', defaultExtension: 'js' },
    'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' }
  };

main.js being whatever file contains your bootstrap function for angular2.

like image 156
Nick Acosta Avatar answered Oct 03 '22 11:10

Nick Acosta