Demandez à Grunt de générer index.html pour différentes configurations


208

J'essaie d'utiliser Grunt comme outil de construction pour ma webapp.

Je veux avoir au moins deux configurations:

Configuration du développement - charger des scripts à partir de fichiers séparés, sans concaténation,

donc mon index.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. Configuration de la production - charge mes scripts minifiés et concaténés dans un seul 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 grogner ces index.html en fonction de la configuration lorsque j'exécute grunt devou grunt prod?

Ou peut-être que je creuse dans la mauvaise direction et qu'il serait plus facile de toujours générer MyApp-all.min.jsmais de mettre dedans tous mes scripts (concaténés) ou un script de chargeur qui charge de manière asynchrone ces scripts à partir de fichiers séparés?

Comment faites-vous, les gars?


3
Essayez l'outil Yeoman, qui comprend une tâche «usemin» qui fait ce que vous avez. De plus, les générateurs Yeamon incluent de nombreuses "bonnes pratiques" faciles à apprendre et difficiles à apprendre lors de l'utilisation d'un nouvel outil.
EricSonaron

Réponses:


161

J'ai récemment découvert ces v0.4.0tâches compatibles avec Grunt :

  • pré-processus de grognement

    Tâche grognante autour du module npm de prétraitement.

  • grunt-env

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

Vous trouverez ci-dessous des extraits de mon Gruntfile.js.

Configuration ENV:

env : {

    options : {

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

    },

    dev: {

        NODE_ENV : 'DEVELOPMENT'

    },

    prod : {

        NODE_ENV : 'PRODUCTION'

    }

},

Prétraitement:

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 %>'
            }

        }

    }

}

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 /src/tmpl/index.htmlfichier modèle (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, bien que ce soit un morceau de code génial, le grunt-usemin de Yeoman est plus robuste que ce dont j'ai personnellement besoin.

REMARQUE: je viens de découvrir les tâches répertoriées ci-dessus aujourd'hui, il se peut donc que je manque une fonctionnalité et / ou que mon processus change en cours de route. Pour l'instant, j'aime la simplicité et les fonctionnalités que grunt-preprocess et grunt-env ont à offrir. :)


Mise à jour de janvier 2014:

Motivé par un vote négatif ...

Lorsque j'ai posté cette réponse, il n'y avait pas beaucoup d'options pour Grunt 0.4.xqui offraient une solution qui répondait à mes besoins. Maintenant, des mois plus tard, je suppose qu'il y a plus d'options qui pourraient être meilleures que celles que j'ai publiées ici. Bien que j'utilise encore personnellement et que j'aime utiliser cette technique pour mes versions , 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.

Mise à jour de février 2014:

Je ne sais pas si cela sera utile à personne, mais j'ai créé ce référentiel de démonstration sur GitHub qui montre une configuration complète (et plus complexe) en utilisant les techniques que j'ai décrites ci-dessus.


Merci, je vais le vérifier!
Dmitry Pashkevich

3
Votre solution m'a fait gagner des heures à me cogner la tête contre un mur. Merci.
sthomps

1
@sthomps Heureux que cela ait aidé! Depuis que j'ai découvert ces tâches, j'ai adoré le flux de travail. Pour info, j'ai apporté une légère modification au processus ... Au lieu de passer plusieurs variables de contexte à mes modèles html, j'ai choisi de passer une var path : '/<%= pkg.name %>/dist/<%= pkg.version %>/<%= now %>/<%= ver %>'qui concatère tous les vars (c'est mon chemin de construction). Sur mon modèle , je vais devoir: <script src="http://cdn.foo.com<!-- @echo path -->/js/bulldog.min.js"></script>. Quoi qu'il en soit, je suis heureux d'avoir pu vous faire gagner du temps! : D
mhulse

4
Vous pouvez faire la même chose en utilisant uniquement grunt-template , en passant simplement un dataobjet différent pour dev / prod.
Mathias Bynens

2
Homme j'aime cette solution .. Elle est propre, lisible et pas trop conçue.
Gaurang Patel

34

J'ai trouvé ma propre solution. Pas encore poli mais je pense que je vais aller dans ce sens.

En gros, j'utilise grunt.template.process () pour générer mon à index.htmlpartir d'un modèle qui analyse la configuration actuelle et produit soit une liste de mes fichiers source d'origine soit des liens vers un seul fichier avec du code minifié. L'exemple ci-dessous concerne les fichiers js, mais la même approche peut être étendue à css et à tout autre fichier texte possible.

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.tmplavec une logique de génération intégrée:

<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. J'ai découvert que Yeoman , qui est basé sur le grognement, a une tâche usemin intégrée qui s'intègre au système de construction de Yeoman. Il génère une version de production de index.html à partir des informations de la version de développement de index.html ainsi que d'autres paramètres d'environnement. Un peu sophistiqué mais intéressant à regarder.


5
grunt-template est un wrapper très légergrunt.template.process()(ce que vous utilisez ici) qui rendrait cela encore plus facile. Vous pouvez faire la même chose en utilisant grunt-template en passant simplement undataobjetdifférentpour dev / prod.
Mathias Bynens

15

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

  • Le problème avec la réponse la plus 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 de correspondance de modèle. Cela signifie que vous devez le mettre à jour à la main dans le Gruntfile.

J'ai compris comment résoudre ces deux problèmes. J'ai configuré ma tâche de grognement de sorte que chaque fois qu'un fichier est ajouté ou supprimé, les balises de script sont automatiquement générées 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 modèle html 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 modèle de globalisation de tous mes fichiers JS. La tâche de surveillance recalcule cette valeur lorsqu'un fichier JS est ajouté ou supprimé.

Maintenant, pour obtenir des résultats différents en mode dev ou prod, il vous suffit de remplir cette variable avec une valeur différente. Voici du 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

jsSrcFileArrayest votre modèle de grognement de fichier grunt typique. jsScriptTagsprend le jsSrcFileArrayet les concatène avec des scriptétiquettes des deux côtés. destPathest 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 sous forme de scriptbalise codée en dur lorsqu'elle est exécutée en prodmode. En mode dev, cette variable se développera à 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>

Faites moi savoir si vous avez des questions.

PS: Ceci 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 transformer cela en un plugin réutilisable. Peut-être que je le ferai un jour.


1
Cela semble prometteur. Avez-vous des chances de partager des extraits?
Adam Marshall

I've set up my grunt task so that every time a file is added or deleted, the script tags automatically get generated to reflect thatComment as-tu fais ça?
CodyBugstein

2
Une autre question: connaissez-vous un moyen de supprimer simplement des blocs de <script>balises HTML ?
CodyBugstein

@Imray pas du haut de ma tête. Vous voulez dire sans aucune forme de modèle (par exemple, grunt-include-replace)? La première pensée qui me vient à l'esprit serait xslt. Mais ce n'est probablement pas une bonne solution.
Daniel Kaplan

1
Cette réponse est sur place, bien que je personnellement retiré destPathde jsScriptTagset troqué grunt.file.expandMappingavec grunt.file.expandles fichiers que je voulais étaient déjà dans les bons endroits. Cela simplifiait beaucoup les choses. Merci @DanielKaplan, vous m'avez fait gagner énormément de temps :)
DanielM

13

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.


2
J'ai vu ce plugin mais je n'aime pas l'idée de spécifier manuellement tous les fichiers (et en fait d'avoir une logique) dans mon index.html car j'ai déjà une liste de fichiers source js / css dans ma configuration grunt et ne le fais pas ' Je ne veux pas me répéter. En fin de compte, ce n'est pas dans index.html où vous devez décider quels fichiers inclure
Dmitry Pashkevich

+1 pour grunt-targethtml. Bien qu'il soit un peu moche d'ajouter des instructions if pour "décider" dans votre index.html quels actifs charger. Pourtant, cela a du sens. C'est l'endroit que vous chercherez normalement à inclure des ressources dans votre projet. De plus, le suivi m'a amené à vérifier grunt-contrib. Il contient de très bonnes choses.
Carbontax

8

Je cherchais une solution plus simple et directe, j'ai donc combiné la réponse de cette question:

Comment placer un bloc if else dans gruntfile.js

et est venu avec les étapes simples suivantes:

  1. Conservez deux versions de vos fichiers d'index comme vous l'avez indiqué et nommez-les index-development.html et index-prodoction.html.
  2. Utilisez la logique suivante dans le bloc concat / copy de votre Gruntfile.js pour votre fichier index.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écutez 'grunt --Release' pour choisir le fichier index-production.html et laissez le drapeau pour avoir la version de développement.

Pas de nouveaux plugins à ajouter ou à configurer et pas de nouvelles tâches de grognement.


3
Le seul inconvénient ici est qu'il y a deux fichiers index.html à maintenir.
Adam Marshall

5

Cette tâche grognante nommée scriptlinker ressemble à un moyen facile d'ajouter les scripts en mode dev. Vous pouvez probablement exécuter une tâche de concaturation d'abord, puis la pointer vers votre fichier concaténé en mode prod.


+1. La documentation est déroutante et certaines choses (appRoot, relative) ne fonctionnent pas toujours comme prévu, mais quand même: outil utile.
hashchange

1
@hashchange Je n'utilise pas cet outil. J'ai fini par utiliser github.com/alanshaw/grunt-include-replace à la place. J'ai une variable dans mon fichier html représentant les balises de script. Ensuite, je remplis cette variable avec une chaîne de code HTML que je veux. En mode dev, cette variable est une liste des scripts. En mode prod, cette variable est la version concaténée et minifiée.
Daniel Kaplan

Merci pour le pointeur sur grunt-include-replace. (J'avais en fait besoin d'un outil pour ajouter tous les fichiers de spécifications d'un répertoire à un fichier Mocha index.html. Scriptlinker est parfait pour cela.)
hashchange

@hashchange vous avez raison sur la succion de la documentation. Comment pouvez-vous lui dire placer les tuiles de script dans votre fichier html?
Daniel Kaplan

1
Vous définissez un commentaire HTML. Jetez un œil à ce fichier . Les insertions ont lieu à <!--SINON COMPONENT SCRIPTS-->et <!--SPEC SCRIPTS-->. Et voici la tâche Grunt qui le fait (un travail réel, par opposition aux trucs dans les documents). J'espère que ça aide;)
hashchange

5

grunt-dom-munger lit et manipule le HTML avec des sélecteurs CSS. Ex. lire les balises de votre html. Supprimez des nœuds, ajoutez des nœuds, etc.

Vous pouvez utiliser grunt-dom-munger pour lire tous vos fichiers JS liés par votre index.html, les uglifier, puis utiliser à nouveau grunt-dom-munger pour modifier votre index.html afin de ne lier que le JS minifié


5

J'ai trouvé un plugin grunt appelé grunt-dev-prod-switch. Tout ce qu'il fait, c'est commenter certains blocs qu'il recherche en fonction d'une option --env que vous passez à grogner (bien que cela vous limite à dev, prod et test).

Une fois que vous l'avez configuré comme expliqué ici , vous pouvez exécuter par exemple:

grunt serve --env=dev, et il ne fait que commenter les blocs qui sont enveloppés par

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

et il décommentera les blocs qui sont enveloppés par

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

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

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

Dans votre cas, ce serait aussi simple que cela:

<!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>

4

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

https://github.com/imaginethepoet/autojqmphonegap

Jetez un œil à mon fichier grunt.coffee:

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

Cela regarde tous les fichiers dans base.html et les aspire pour créer des travaux index.html fantastiques pour les applications multipages (phonegap). Cela permet un développement plus facile car tous les développeurs ne travaillent pas sur une seule application longue page (empêchant de nombreux enregistrements de conflits). Au lieu de cela, vous pouvez diviser les pages et travailler sur de plus petits morceaux de code et compiler sur la page complète à l'aide d'une commande watch.

Bake lit le modèle à partir de base.html et injecte les pages html des composants sur la montre.

<!DOCTYPE html>

Démonstrations jQuery Mobile

app.initialize ();

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

Vous pouvez aller un peu plus loin et ajouter des injections dans vos pages pour les "menus", les "popups", etc., afin de pouvoir vraiment diviser les pages en composants plus petits et gérables.


Peut-être pouvez-vous améliorer votre réponse avec une démo de code qui utilise grunt-bake?
Dmitry Pashkevich

4

Utilisez une combinaison de wiredep https://github.com/taptapship/wiredep et usemin https://github.com/yeoman/grunt-usemin afin que grunt s'occupe de ces tâches. Wiredep ajoutera vos dépendances un fichier de script à la fois, et usemin les concaténera toutes en un seul fichier pour la production. Cela peut ensuite être accompli avec seulement quelques commentaires html. Par exemple, mes packages bower sont automatiquement inclus et ajoutés au html lorsque je lance bower install && grunt bowerInstall:

<!-- build:js /scripts/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->

2

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

Utiliser le modèle Jade ... passer des variables à un modèle Jade est un cas d'utilisation standard

J'utilise grunt (grunt-contrib-jade) mais vous n'avez pas besoin d'utiliser grunt. Utilisez simplement le module jade npm standard.

Si vous utilisez grunt, votre fichier grunt aimerait 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 désormais accéder facilement aux données transmises par grunt dans le modèle Jade.

Tout comme l'approche utilisée par Modernizr, j'ai défini 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à selon que la classe CSS est présente ou non.

C'est très bien si vous utilisez Angular car vous pouvez faire des ng-if pour inclure des éléments dans la page selon que 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 le développement mais pas dans la production)

<script ng-if="controller.isClassPresent()" src="//localhost:35729/livereload.js"></script> 

2

Considérez processhtml . Il permet de définir plusieurs "cibles" pour les builds. Les commentaires sont utilisés pour inclure ou exclure conditionnellement des éléments du HTML:

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

devient

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

Il prétend même faire des trucs astucieux comme ça (voir le LISEZMOI ):

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

<!-- class is changed to 'production' only when the 'dist' build is executed -->
<html class="production">
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.