Odyssey #3 - Migration de SystemJS vers Webpack

écrit par Rémy Villain publié le jeudi 13 juillet 2017

La mise en orbite s'effectue non sans mal. Après avoir perdu des heures à configuer ce satané SystemJS, nous avons fini par décider de l'abandonner, et d'adopter Webpack qui est plébiscité par la plupart des développeurs Angular. Nous en avons aussi profité pour passer de Visual Studio 2015 à 2017 qui intègre beaucoup mieux typescript. Dans cet article je vais simplement faire un petit topo sur les quelques pré-requis à l'utilisation de WebPack dans VS2017. Pour tout ce qui est code pur, je vous conseille fortement cet article : http://angularfirst.com/systemjs-to-webpack-before-you-begin/ Voici le guide d'utilisation de Webpack pour Angular dans un projet MVC dans Visual Studio 2017 :

  1. Ajouter les packages suivants
    {
      ...
      "dependencies": {
       ...
        "core-js": "^2.4.1",
        "es6-promise": "^4.0.5",
        "es6-shim": "^0.35.3",
        "rxjs": "^5.0.3",
        "zone.js": "^0.7.4"
      },
      "devDependencies": {
        ...
        "awesome-typescript-loader": "^3.1.2",
        "webpack": "^2.3.3",
        "clean-webpack-plugin": "^0.1.15",
        "copy-webpack-plugin": "^4.0.1",
        "html-webpack-plugin": "^2.28.0",
        "karma-webpack": "^2.0.3",
        "jasmine-core": "^2.5.2",
        "raw-loader": "^0.5.1",
        ...
      }
    }
  2. Installer l'extension WebPack Task Runner pour exécuter Webpack depuis l'explorateur d'exécuteur de tâche
  3. Installer l'extension NPM Task Runner pour installer et mettre à jour les packages npm plus facilement
  4. Configurer webpack pour transpiler le typescript en 2 fichiers js : libs/app.bundle.js et libs/vendor.bundle.js et copier certains assets dans le répertoire libs/assets.
    var path = require('path');
    var webpack = require('webpack');
    
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var CopyWebpackPlugin = require('copy-webpack-plugin');
    var CleanWebpackPlugin = require('clean-webpack-plugin');
    
    var ignore = new webpack.IgnorePlugin(new RegExp("/(^fs$|cptable|jszip|xlsx|xls|^es6-promise$|^net$|^tls$|^forever-agent$|^tough-cookie$|cpexcel|^path$)/"))
    
    console.log('@@@@@@@@@ USING DEVELOPMENT @@@@@@@@@@@@@@@');
    
    module.exports = {
    
        devtool: 'source-map',
        performance: {
            hints: false
        },
        entry: {
            'app': './tsScripts/main.ts',
            'vendor': [
                'core-js/client/shim.min.js',
                'zone.js/dist/zone',
                '@angular/common',
                '@angular/compiler',
                '@angular/core',
                '@angular/http',
                '@angular/platform-browser',
                '@angular/platform-browser-dynamic',
                'rxjs',
                'ng2-table',
                'ng2-bootstrap'
            ]
        },
        output: {
            path: __dirname + '/libs/',
            filename: '[name].bundle.js',
            chunkFilename: '[id].chunk.js',
        },
        resolve: {
            extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html'],
            alias: {
                // bind version of jquery-ui
                "jquery-ui": "jquery-ui/ui",
                // bind to modules;
                modules: path.join(__dirname, "node_modules"),
            }
        },
        externals: [
            {
                './cptable': 'var cptable'
            }
        ],
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loaders: [
                        'awesome-typescript-loader'
                    ]
                },
                {
                    test: /\.(png|jpg|gif|woff|woff2|ttf|svg|eot)$/,
                    loader: 'file-loader?name=assets/[name]-[hash:6].[ext]'
                },
                {
                    test: /favicon.ico$/,
                    loader: 'file-loader?name=/[name].[ext]'
                },
                {
                    test: /\.css$/,
                    loader: 'style-loader!css-loader'
                },
                {
                    test: /\.scss$/,
                    exclude: /node_modules/,
                    loaders: ['style-loader', 'css-loader', 'sass-loader']
                },
                {
                    test: /\.html$/,
                    loader: 'raw-loader'
                },
                {
                    test: /\.exec.js$/,
                    use: ['script-loader']
                }
            ],
            exprContextCritical: false
        },
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({ name: ['vendor', 'polyfills'] }),
    
            new CleanWebpackPlugin(
                [
                    './libs',
                ]
            ),
    
            new CopyWebpackPlugin([
                { from: './node_modules/ng2-toaster/bundles/*.css', to: './assets', flatten: true },
                { from: './node_modules/gridstack/dist/*.css', to: './assets', flatten: true },
                { from: './node_modules/underscore/underscore.js', to: './assets', flatten: true },
                { from: './node_modules/gridstack/dist/gridstack.js', to: './assets', flatten: true },
                { from: './node_modules/jquery-ui-bundle/jquery-ui.css', to: './assets', flatten: true },
                { from: './node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.js', to: './assets', flatten: true },
                { from: './node_modules/ng2-toastr/bundles/ng2-toastr.min.css', to: './assets', flatten: true },
                { from: './node_modules/bootstrap-confirmation2/bootstrap-confirmation.min.js', to: './assets', flatten: true }
            ]),
            ignore
        ]
    
    };
    
    
  5. Ajouter les assets aux bundles MVC :
    using System.Web;
    using System.Web.Optimization;
    
    namespace AngularOdyssey
    {
        public class BundleConfig
        {
            // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
            public static void RegisterBundles(BundleCollection bundles)
            {
                bundles.Add(new ScriptBundle("~/bundles/ext-js").Include(
                            "~/Scripts/jquery-{version}.js",
                            "~/Scripts/jquery-ui-{version}.js",
                            "~/Scripts/bootstrap.js",
                            "~/Scripts/respond.js",
                            "~/libs/assets/bootstrap-confirmation.min.js",
                            "~/libs/assets/jquery.ui.touch-punch.js",
                            "~/libs/assets/underscore.js",
                            "~/libs/assets/gridstack.js",
                            "~/libs/ng2-toastr.min.js"
                            ));
    
                // Use the development version of Modernizr to develop with and learn from. Then, when you're
                // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
                bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                            "~/Scripts/modernizr-*"));
    
                bundles.Add(new StyleBundle("~/Styles/css").Include(
                          "~/libs/assets/ng2-toastr.min.css",
                          "~/libs/assets/gridstack.css",
                          "~/libs/assets/gridstack-extra.css"));
                // Style less
                bundles.Add(new LessBundle("~/Styles/less")
                    .Include("~/Content/less/global.less", new CssRewriteUrlTransform()));
    #if !DEBUG
                BundleTable.EnableOptimizations = true;
    #endif
            }
        }
    }
    
  6. Inclure les fichiers js dans la page index
    ...
    <base href="/">
    <odyssey-app>
        <div class="splash">
            <h1>Chargement...</h1>
        </div>
    </odyssey-app>
    ...
    <script type="text/javascript" src="~/libs/polyfills.bundle.js"></script>
    <script type="text/javascript" src="~/libs/vendor.bundle.js"></script>
    <script type="text/javascript" src="~/libs/app.bundle.js"></script>
  7. Remplacer tous les templateUrl de l'application par template.
    @Component({
        moduleId: module.id,
        templateUrl: './user.edit.component.html'
    })
    @Component({
        template: require('./user.edit.component.html')
    })
     

Avec Webpack, nous n'avons plus rencontré de problèmes au moment de l'ajout de package, la transpilation est plus rapide et la configuration est beaucoup plus simple que celle de SystemJS. Webpack permet aussi de se passer de gulp et ça personne ne va s'en plaindre. Visual Studio 2017 nous permet nativement de débuguer le TypeScript et gère à merveille l'autocomplétion. En bref on s'est sacrément simplifié la vie avec ces migrations, je ne peux que vous conseiller de faire de même. Evidement, pour récuperer le projet, c'est toujours par içi que ça se passe.