Que Grunt génère index.code html pour différentes configurations

j'essaie D'utiliser Grunt comme outil de construction pour mon webapp.

je veux avoir au moins deux configurations:

I. Development setup - charger des scripts à partir de fichiers séparés, sans concaténation,

c'est mon Indice.html ressemblerait à quelque chose comme:

<!DOCTYPE html>
<html>
    <head>
        <script src="js/module1.js" />
        <script src="js/module2.js" />
        <script src="js/module3.js" />
        ...
    </head>
    <body></body>
</html>

II. Installation de Production - charger mes scripts miniified & concatenated dans un fichier,

avec index.html en conséquence:

<!DOCTYPE html>
<html>
    <head>
        <script src="js/MyApp-all.min.js" />
    </head>
    <body></body>
</html>

la question Est, Comment puis-je faire grunt faire ces index.les html dépendent de la configuration quand j'exécute grunt dev ou grunt prod ?

ou peut-être que je creuse dans la mauvaise direction et il serait plus facile de toujours générer MyApp-all.min.js mais mettez dedans soit tous mes scripts (concaténés) ou un script loader qui charge asynchrone ces scripts de fichiers séparés?

Comment faites-vous, les gars?

206
demandé sur Dmitry Pashkevich 2012-09-13 12:08:55

12 réponses

j'ai récemment découvert ces grognements v0.4.0 tâches compatibles:

  • grunt-préprocess

    tâche de Grunt autour du module npm préprocesseur.

  • grunt-env

    tâche Grunt pour automatiser la configuration de l'environnement pour les tâches futures.

ci-dessous sont des extraits de mon Gruntfile.js .

env Setu:

env : {

    options : {

        /* Shared Options Hash */
        //globalOption : 'foo'

    },

    dev: {

        NODE_ENV : 'DEVELOPMENT'

    },

    prod : {

        NODE_ENV : 'PRODUCTION'

    }

},

Préprocessus:

preprocess : {

    dev : {

        src : './src/tmpl/index.html',
        dest : './dev/index.html'

    },

    prod : {

        src : './src/tmpl/index.html',
        dest : '../<%= pkg.version %>/<%= now %>/<%= ver %>/index.html',
        options : {

            context : {
                name : '<%= pkg.name %>',
                version : '<%= pkg.version %>',
                now : '<%= now %>',
                ver : '<%= ver %>'
            }

        }

    }

}
"151980920 des" Tâches":

grunt.registerTask('default', ['jshint']);

grunt.registerTask('dev', ['jshint', 'env:dev', 'clean:dev', 'preprocess:dev']);

grunt.registerTask('prod', ['jshint', 'env:prod', 'clean:prod', 'uglify:prod', 'cssmin:prod', 'copy:prod', 'preprocess:prod']);

et dans le fichier de modèles /src/tmpl/index.html (par exemple):

<!-- @if NODE_ENV == 'DEVELOPMENT' -->

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
    <script src="../src/js/foo1.js"></script>
    <script src="../src/js/foo2.js"></script>
    <script src="../src/js/jquery.blah.js"></script>
    <script src="../src/js/jquery.billy.js"></script>
    <script src="../src/js/jquery.jenkins.js"></script>

<!-- @endif -->

<!-- @if NODE_ENV == 'PRODUCTION' -->

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

    <script src="http://cdn.foo.com/<!-- @echo name -->/<!-- @echo version -->/<!-- @echo now -->/<!-- @echo ver -->/js/<!-- @echo name -->.min.js"></script>

<!-- @endif -->

je suis sûr que ma configuration est différente de la plupart des gens, et l'utilité de ce qui précède dépendra de votre situation. Pour moi, alors que c'est un morceau impressionnant de code, le Yeoman grunt-usemin est plus robuste que je n'ai personnellement besoin.

NOTE: I juste découvert les tâches énumérées ci-dessus aujourd'hui, il se peut donc que je manque une caractéristique et/ou mon processus peut changer plus tard. Pour l'instant, j'aime la simplicité et caractéristiques que grunt-préprocess et grunt-env ont à offrir. :)


Jan 2014 mise à jour:

motivé par un vote négatif ...

quand j'ai posté cette réponse il n'y avait pas beaucoup d'options pour Grunt 0.4.x qui a offert une solution qui a fonctionné pour mes besoins. Maintenant, des mois plus tard, je suppose qu'il y a plus d'options là-bas que pourrait être mieux que ce que j'ai posté ici. alors que j'utilise encore personnellement, et que j'aime utiliser, cette technique pour mes constructions , je demande aux futurs lecteurs de prendre le temps de lire les autres réponses données et de rechercher toutes les options. si vous trouvez une meilleure solution, veuillez poster votre réponse ici.

Feb 2014 mise à jour:

Je ne suis pas sûr que ce soit utile à quiconque, mais j'ai créé ce dépôt de démonstration sur GitHub cela montre une configuration complète (et plus complexe) en utilisant la(Les) technique (s) que j'ai décrite ci-dessus.

161
répondu mhulse 2014-02-23 01:24:57

j'ai trouvé ma propre solution. Pas poli mais je pense que je vais aller dans cette direction.

à essense, j'utilise grunt.modèle.process() pour générer mon index.html à partir d'un modèle, qui analyse la configuration actuelle et produit une liste de mes fichiers source d'origine ou des liens vers un seul fichier avec minifiés code. L'exemple ci-dessous est pour les fichiers js mais la même approche peut être étendue à css et à n'importe quel autre des fichiers texte possibles.

grunt.js :

/*global module:false*/
module.exports = function(grunt) {
    var   // js files
        jsFiles = [
              'src/module1.js',
              'src/module2.js',
              'src/module3.js',
              'src/awesome.js'
            ];

    // Import custom tasks (see index task below)
    grunt.loadTasks( "build/tasks" );

    // Project configuration.
    grunt.initConfig({
      pkg: '<json:package.json>',
      meta: {
        banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
          '<%= grunt.template.today("yyyy-mm-dd") %> */'
      },

      jsFiles: jsFiles,

      // file name for concatenated js
      concatJsFile: '<%= pkg.name %>-all.js',

      // file name for concatenated & minified js
      concatJsMinFile: '<%= pkg.name %>-all.min.js',

      concat: {
        dist: {
            src: ['<banner:meta.banner>'].concat(jsFiles),
            dest: 'dist/<%= concatJsFile %>'
        }
      },
      min: {
        dist: {
        src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],
        dest: 'dist/<%= concatJsMinFile %>'
        }
      },
      lint: {
        files: ['grunt.js'].concat(jsFiles)
      },
      // options for index.html builder task
      index: {
        src: 'index.tmpl',  // source template file
        dest: 'index.html'  // destination file (usually index.html)
      }
    });


    // Development setup
    grunt.registerTask('dev', 'Development build', function() {
        // set some global flags that all tasks can access
        grunt.config('isDebug', true);
        grunt.config('isConcat', false);
        grunt.config('isMin', false);

        // run tasks
        grunt.task.run('lint index');
    });

    // Production setup
    grunt.registerTask('prod', 'Production build', function() {
        // set some global flags that all tasks can access
        grunt.config('isDebug', false);
        grunt.config('isConcat', true);
        grunt.config('isMin', true);

        // run tasks
        grunt.task.run('lint concat min index');
    });

    // Default task
    grunt.registerTask('default', 'dev');
};

index.js (the index task) :

module.exports = function( grunt ) {
    grunt.registerTask( "index", "Generate index.html depending on configuration", function() {
        var conf = grunt.config('index'),
            tmpl = grunt.file.read(conf.src);

        grunt.file.write(conf.dest, grunt.template.process(tmpl));

        grunt.log.writeln('Generated \'' + conf.dest + '\' from \'' + conf.src + '\'');
    });
}

enfin, index.tmpl , avec la logique de génération cuit dans:

<doctype html>
<head>
<%
    var jsFiles = grunt.config('jsFiles'),
        isConcat = grunt.config('isConcat');

    if(isConcat) {
        print('<script type="text/javascript" src="' + grunt.config('concat.dist.dest') + '"></script>\n');
    } else {
        for(var i = 0, len = jsFiles.length; i < len; i++) {
            print('<script type="text/javascript" src="' + jsFiles[i] + '"></script>\n');
        }
    }
%>
</head>
<html>
</html>

UPD. découvert que Yeoman , qui est basé sur grunt, a un intégré usemin tâche qui s'intègre avec le système de construction de Yeoman. Il génère une version de production d'index.html à partir de l'information dans le développement de la version de l'index.html ainsi que d'autres paramètres de l'environnement. Un peu sophistiqué mais intéressant à regarder.

34
répondu Dmitry Pashkevich 2012-12-15 22:58:27

Je n'aime pas les solutions ici (y compris celui que j'ai précédemment donné ) et voici pourquoi:

  • le problème avec la réponse la plus haute votée est que vous devez synchroniser manuellement la liste des balises de script lorsque vous ajoutez/renommez/supprimez un fichier JS.
  • le problème avec la réponse acceptée est que votre liste de fichiers JS ne peut pas avoir la correspondance de modèle. Ce ce qui veut dire que vous devez le mettre à jour à la main dans le fichier Gruntfile.

j'ai trouvé comment résoudre ces deux problèmes. J'ai mis en place ma tâche grunt de sorte que chaque fois qu'un fichier est ajouté ou supprimé, les balises de script sont automatiquement générés pour refléter cela. De cette façon, vous n'avez pas besoin de modifier votre fichier html ou votre fichier grunt lorsque vous ajoutez/supprimez/renommez vos fichiers JS.

pour résumer comment cela fonctionne, j'ai un html modèle avec une variable pour les balises de script. J'utilise https://github.com/alanshaw/grunt-include-replace pour remplir cette variable. En mode dev, cette variable provient d'un motif globbing de tous mes fichiers JS. La montre tâche recalcule cette valeur lorsqu'un fichier JS est ajouté ou supprimé.

maintenant, pour obtenir des résultats différents en mode dev ou prod, vous remplissez simplement cette variable avec une valeur différente. Voici un code:

var jsSrcFileArray = [
    'src/main/scripts/app/js/Constants.js',
    'src/main/scripts/app/js/Random.js',
    'src/main/scripts/app/js/Vector.js',
    'src/main/scripts/app/js/scripts.js',
    'src/main/scripts/app/js/StatsData.js',
    'src/main/scripts/app/js/Dialog.js',
    'src/main/scripts/app/**/*.js',
    '!src/main/scripts/app/js/AuditingReport.js'
];

var jsScriptTags = function (srcPattern, destPath) {
    if (srcPattern === undefined) {
        throw new Error("srcPattern undefined");
    }
    if (destPath === undefined) {
        throw new Error("destPath undefined");
    }
    return grunt.util._.reduce(
        grunt.file.expandMapping(srcPattern, destPath, {
            filter: 'isFile',
            flatten: true,
            expand: true,
            cwd: '.'
        }),
        function (sum, file) {
            return sum + '\n<script src="' + file.dest + '" type="text/javascript"></script>';
        },
        ''
    );
};

...

grunt.initConfig({

    includereplace: {
        dev: {
            options: {
                globals: {
                    scriptsTags: '<%= jsScriptTags(jsSrcFileArray, "../../main/scripts/app/js")%>'
                }
            },
            src: [
                'src/**/html-template.html'
            ],
            dest: 'src/main/generated/',
            flatten: true,
            cwd: '.',
            expand: true
        },
        prod: {
            options: {
                globals: {
                    scriptsTags: '<script src="app.min.js" type="text/javascript"></script>'
                }
            },
            src: [
                'src/**/html-template.html'
            ],
            dest: 'src/main/generatedprod/',
            flatten: true,
            cwd: '.',
            expand: true
        }

...

    jsScriptTags: jsScriptTags

jsSrcFileArray est votre modèle typique de globbooking de fichiers. jsScriptTags prend le jsSrcFileArray et les concaténate avec des étiquettes script sur les deux côtés. destPath est le préfixe que je veux sur chaque fichier.

et voici à quoi ressemble le HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Example</title>

</head>

<body>    
@@scriptsTags
</body>
</html>

maintenant, comme vous pouvez le voir dans la configuration, je génère la valeur de cette variable comme une étiquette codée script quand elle est exécutée en mode prod . En dev mode, cette variable va s'étendre à une valeur comme celle-ci:

<script src="../../main/scripts/app/js/Constants.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Random.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Vector.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/StatsData.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Dialog.js" type="text/javascript"></script>

dites-moi si vous avez des questions.

PS: c'est une quantité folle de code pour quelque chose que je voudrais faire dans chaque application js côté client. J'espère que quelqu'un pourra en faire un plugin réutilisable. Je vais peut-être un jour.

15
répondu Daniel Kaplan 2017-05-23 10:31:20

je me pose la même question depuis un moment, et je pense que ce plugin grunt pourrait être configuré pour faire ce que vous voulez: https://npmjs.org/package/grunt-targethtml . Il implémente des balises HTML conditionnelles, qui dépendent de la cible grunt.

13
répondu Per Quested Aronsson 2012-10-01 20:27:51

j'étais à la recherche d'une solution plus simple et plus directe, donc j'ai combiné la réponse de cette question:

Comment passer si le reste bloquer dans gruntfile.js

et est venu avec les étapes simples suivantes:

  1. conservez deux versions de vos fichiers index tels que vous les avez énumérés et nommez-les index-development.html et index-prodocution.HTML.
  2. utilisez ce qui suit: la logique dans votre Gruntfile.le bloc concat/copie de js pour votre index.fichier html:

    concat: {
        index: {
            src : [ (function() {
                if (grunt.option('Release')) {
                  return 'views/index-production.html';
                } else {
                  return 'views/index-development.html';
                }
              }()) ],
           dest: '<%= distdir %>/index.html',
           ...
        },
        ...
    },
    
  3. exécuter "grognement --Version" afin de choisir l'indice de la production.fichier html et de laisser sur le drapeau pour avoir la version de développement.

pas de nouveaux plugins pour ajouter ou configurer et pas de nouvelles tâches grunt.

7
répondu Edward T 2017-05-23 12:10:40

cette tâche grunt nommée scriptlinker ressemble à un moyen facile d'ajouter les scripts en mode dev. Vous pouvez probablement exécuter une tâche concat d'abord et ensuite la pointer vers votre fichier concaténé en mode prod.

5
répondu Daniel Kaplan 2013-08-19 17:43:34

grunt-dom-munger lit et manipule HTML avec des sélecteurs CSS. Ex. lisez les balises de votre html. Supprimer les noeuds, ajouter des noeuds, et plus encore.

vous pouvez utiliser grunt-dom-munger pour lire tous vos fichiers JS qui sont liés par votre index.html, uglify et ensuite utiliser grunt-dom-munger à nouveau de modifier votre index.code html pour seul lien le minifiés JS

5
répondu brillout 2013-12-21 15:54:55

j'ai trouvé un plugin grunt appelé grunt-dev-prod-switch. Tout ce qu'il fait est de commenter certains blocs qu'il recherche sur la base d'une option --env que vous passez à grunt (bien qu'il vous limite à dev, prod, et test).

une fois que vous l'avez configuré comme il explique ici , vous pouvez exécuter par exemple:

grunt serve --env=dev , et il n'est de commenter les blocs qui sont enveloppés par

    <!-- env:test/prod -->
    your code here
    <!-- env:test/prod:end -->

et il sera démontez les blocs qui sont enveloppés par

    <!-- env:dev -->
    your code here
    <!-- env:dev:end -->

il fonctionne aussi sur javascript, je l'utilise pour configurer la bonne adresse IP à laquelle me connecter pour mon API backend. Les blocs changent en

    /* env:dev */
    your code here
    /* env:dev:end */

dans votre cas, ce serait aussi simple que ceci:

<!DOCTYPE html>
<html>
    <head>
        <!-- env:dev -->
        <script src="js/module1.js" />
        <script src="js/module2.js" />
        <script src="js/module3.js" />
        ...
        <!-- env:dev:end -->
        <!-- env:prod -->
        <script src="js/MyApp-all.min.js" />
        ...
        <!-- env:prod:end -->
    </head>
    <body></body>
</html>
5
répondu anonymous 2015-04-08 13:00:59

grunt-bake est un script Grunt fantastique qui fonctionnerait très bien ici. Je l'utilise dans mon script de construction automatique JQM.

https://github.com/imaginethepoet/autojqmphonegap

regardez mon grunt.café de fichier:

bake:
    resources: 
      files: "index.html":"resources/custom/components/base.html"

ceci regarde tous les fichiers de la base.html et les aspire pour créer index.html fonctionne fantastique pour les applications de multipage (phonegap). Cela permet un développement plus facile que tous les devs ne travaillent pas sur une longue application d'une seule page (empêchant beaucoup de checkins de conflit). Au lieu de cela, vous pouvez séparer les pages et travailler sur de plus petits morceaux de code et compiler à la pleine page en utilisant une commande watch.

Bake lit le modèle de la base.html et injecte le composant html des pages sur la montre.

<!DOCTYPE html>

jQuery Mobile Demos

App.initialiser();

<body>
    <!--(bake /resources/custom/components/page1.html)-->
    <!--(bake /resources/custom/components/page2.html)-->
    <!--(bake /resources/custom/components/page3.html)-->
</body>

vous pouvez prendre ceci un peu plus loin et ajouter des injections dans vos pages pour" menus "" popups " etc donc vous pouvez vraiment casser les pages en plus petits composants gérables.

4
répondu imaginethepoet 2013-12-16 19:47:46

utilisez une combinaison de wiredep https://github.com/taptapship/wiredep et usemin https://github.com/yeoman/grunt-usemin pour que grunt s'occupe de ces tâches. Wiredep ajoutera vos dépendances un fichier de script à la fois, et usemin les concaténera tous en un seul fichier pour la production. Ceci peut être accompli avec juste quelques commentaires html. Par exemple, mes paquets bower sont automatiquement inclus et ajoutés à le html quand j'exécute bower install && grunt bowerInstall :

<!-- build:js /scripts/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->
4
répondu Scottux 2014-06-09 19:57:08

prendre en considération processhtml . Il permet de définir plusieurs "cibles" pour les versions. Les commentaires sont utilisés pour inclure ou exclure conditionnellement du matériel du HTML:

<!-- build:js:production js/app.js -->
...
<!-- /build -->

devient

<script src="js/app.js"></script>

il prétend même faire des choses nifty comme ceci (voir le README ):

<!-- build:[class]:dist production -->
<html class="debug_mode">
<!-- /build -->

<!-- class is changed to 'production' only when the 'dist' build is executed -->
<html class="production">
2
répondu dat 2017-02-20 19:39:02

Cette réponse n'est pas pour les noobs!

utilisez Jade templating ... passer des variables à un modèle Jade est un cas d'utilisation standard de bog

j'utilise grunt (grunt-contrib-jade) mais vous n'avez pas à utiliser grunt. Il suffit d'utiliser le module Jade npm standard.

si vous utilisez grunt alors votre fichier gruntfile voudrait quelque chose comme ...

jade: {
    options: {
      // TODO - Define options here
    },
    dev: {
      options: {
        data: {
          pageTitle: '<%= grunt.file.name %>',
          homePage: '/app',
          liveReloadServer: liveReloadServer,
          cssGruntClassesForHtmlHead: 'grunt-' + '<%= grunt.task.current.target %>'
        },
        pretty: true
      },
      files: [
        {
          expand: true,
          cwd: "src/app",
          src: ["index.jade", "404.jade"],
          dest: "lib/app",
          ext: ".html"
        },
        {
          expand: true,
          flatten: true,
          cwd: "src/app",
          src: ["directives/partials/*.jade"],
          dest: "lib/app/directives/partials",
          ext: ".html"
        }
      ]
    }
  },

nous pouvons maintenant accéder facilement aux données passées par grunt dans le Jade modèle.

tout comme L'approche utilisée par Modernizr, j'ai mis une classe CSS sur la balise HTML en fonction de la valeur de la variable passée et je peux utiliser la logique JavaScript à partir de là en fonction de la présence ou non de la classe CSS.

c'est génial si vous utilisez Angular depuis que vous pouvez faire ng-if pour inclure des éléments dans la page basée sur si la classe est présente.

Par exemple, je pourrais inclure un script si la classe est présente ...

(par exemple, je pourrais inclure le script de rechargement en direct dans dev mais pas en production)

<script ng-if="controller.isClassPresent()" src="//localhost:35729/livereload.js"></script> 
1
répondu danday74 2015-06-30 10:27:43