Concater les scripts dans l'ordre avec Gulp

192

Disons, par exemple, que vous construisez un projet sur Backbone ou autre et que vous devez charger des scripts dans un certain ordre, par exemple underscore.js doit être chargé avantbackbone.js .

Comment puis-je faire concaténer les scripts afin qu'ils soient dans l'ordre?

// JS concat, strip debugging and minify
gulp.task('scripts', function() {
    gulp.src(['./source/js/*.js', './source/js/**/*.js'])
    .pipe(concat('script.js'))
    .pipe(stripDebug())
    .pipe(uglify())
    .pipe(gulp.dest('./build/js/'));
});

J'ai le bon ordre de scripts dans mon source/index.html, mais comme les fichiers sont organisés par ordre alphabétique, gulp concatent underscore.jsaprès backbone.js, et l'ordre des scripts dans monsource/index.html n'a pas d'importance, il regarde les fichiers dans le répertoire.

Alors, quelqu'un a-t-il une idée à ce sujet?

Meilleure idée que j'ai est de renommer les scripts de fournisseurs avec 1, 2,3 pour leur donner l'ordre, mais je ne suis pas sûr si je comme ça.

Au fur et à mesure que j'en apprenais plus, j'ai trouvé que Browserify était une excellente solution, cela peut être pénible au début, mais c'est génial.

Michael Joseph Aubry
la source
4
Je pourrais mentionner que de nos jours, j'utilise browserify. Il a sa propre petite courbe d'apprentissage IMO. J'ai eu du mal au début, mais gulp browserify est une bonne façon de procéder! Permettre à votre code d'être modulaire! Vous gérez la commande dans un shim, donc la concaténation n'est pas nécessaire lors de l'utilisation de browserify.
Michael Joseph Aubry
Vous souhaitez donner plus de détails sur votre solution ou un lien?
Dmitri Zaitsev
kroltech.com/2013/12/... voici un lien vers un projet standard qui m'a vraiment aidé à démarrer avec une bonne gestion de projet. Après avoir souffert d'apprendre tout cela, je peux mieux gérer mes projets. Il a le projet sur github et vous pouvez voir comment il utilise browserify. Youtube aide toujours et bien sûr la source elle-même est toujours sous-estimée github.com/substack/node-browserify#usage
Michael Joseph Aubry
Fondamentalement, l'idée est de pouvoir utiliser npm comme la syntaxe avec requiresur le front-end car bien sûr, si vous avez utilisé npm sur votre serveur, vous voyez comment vous pouvez exiger des modules, mais browserify vous permet de le faire sur le code côté client, gardez en tête, pour commencer, il faut un peu de bricolage, mais c'est principalement à l'intérieur du package.json et si vous voulez l'utiliser avec gulp.js ou grunt.js. Si vous installez le package browserify gulp / grunt, vous pouvez exécuter gulp/grunt browserifyet transformer votre script en un script principal, c'est une légère courbe d'apprentissage mais ça vaut le coup IMO.
Michael Joseph Aubry
Merci! En fait, je suis tombé sur un excellent article medium.com/@dickeyxxx/ ... faisant un bon point que vous n'avez pas vraiment besoin browserifyde Angularmodules, où la simple concaténation fonctionne et l'ordre n'a pas d'importance :)
Dmitri Zaitsev

Réponses:

199

J'ai eu un problème similaire récemment avec Grunt lors de la création de mon application AngularJS. Voici une question que j'ai postée.

Ce que j'ai fini par faire, c'est de lister explicitement les fichiers dans l'ordre dans la configuration grunt. Le fichier de configuration ressemblera alors à ceci:

[
  '/path/to/app.js',
  '/path/to/mymodule/mymodule.js',
  '/path/to/mymodule/mymodule/*.js'
]

Grunt est capable de déterminer quels fichiers sont des doublons et de ne pas les inclure. La même technique fonctionnera également avec Gulp.

Chad Johnson
la source
74
Cela fonctionne aussi bien sous gulp, soit dit en passant. Gulp ne répétera pas non plus les fichiers.
OverZealous
1
Cool les gars, ces deux chefs-d'œuvre sont géniaux. Je viens de configurer mon fichier gulp.js pour qu'il fonctionne comme je le souhaite, d'écrire dans du html, de sauvegarder le fichier et de créer un site construit avec les meilleurs frameworks et bonnes pratiques en appuyant simplement sur un bouton. De plus, les mises à jour seront faciles, si vous n'utilisez aucune de celles dont vous avez besoin!
Michael Joseph Aubry
1
Oui! J'ai commencé à utiliser Grunt récemment, et c'est génial. Plus besoin d'incorporer des applications JavaScript dans des frameworks de backends.
Chad Johnson
3
Gulp dupliquait des fichiers dans ma tentative, mais j'ai réalisé que le cas était différent dans gulp par rapport au système de fichiers, alors faites attention à cela! Avec la casse exacte, gulp ne dupliquera pas les fichiers.
Chris
2
La commande manuelle est un cauchemar dans un projet sérieux. Il existe des trieurs de fichiers spéciaux - pour angularjs et autres.
zhekaus
135

Une autre chose qui aide si vous avez besoin que certains fichiers viennent après un blob de fichiers, est d'exclure des fichiers spécifiques de votre glob, comme ceci:

[
  '/src/**/!(foobar)*.js', // all files that end in .js EXCEPT foobar*.js
  '/src/js/foobar.js',
]

Vous pouvez combiner cela avec la spécification des fichiers qui doivent venir en premier, comme expliqué dans la réponse de Chad Johnson.

Trop zélé
la source
Ah j'ai en fait vu votre message plus tôt et cela m'a aidé à injecter du code dans mon srcet à mon buildje vous ai vu utiliser cette syntaxe, j'ai fini par effacer cette partie parce que je ne savais pas exactement pourquoi j'en avais besoin, cela a du sens maintenant.
Michael Joseph Aubry
Oh d'accord, donc votre point ici vient de me frapper, est-ce que cela ne ferait pas durer foobar.js? Cela ne devrait pas être l'inverse. ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Michael Joseph Aubry
Ouais, c'était plus juste un peu d'aide supplémentaire. C'est plus utile lorsque vous avez besoin ou que vous souhaitez que le code de votre application principale entre après que tout le reste est chargé. Il n'y a aucune raison de l'utiliser ( !(foobar)) si vous avez préalablement inclus un fichier spécifique.
OverZealous
Pour une application AngularJS où mes définitions de module résident dans des fichiers qui n'ont pas de tiret dans le nom, ce modèle Gulp glob a fonctionné; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Sam T
17

J'ai utilisé le plugin gulp-order mais il n'est pas toujours réussi comme vous pouvez le voir par mon module de nœud post -commande de débordement de pile avec des flux fusionnés . En parcourant les documents Gulp, je suis tombé sur le module streamque qui a très bien fonctionné pour spécifier l'ordre de la concaténation dans mon cas. https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Un exemple de la façon dont je l'ai utilisé est ci-dessous

var gulp         = require('gulp');
var concat       = require('gulp-concat');
var handleErrors = require('../util/handleErrors');
var streamqueue  = require('streamqueue');

gulp.task('scripts', function() {
    return streamqueue({ objectMode: true },
        gulp.src('./public/angular/config/*.js'),
        gulp.src('./public/angular/services/**/*.js'),
        gulp.src('./public/angular/modules/**/*.js'),
        gulp.src('./public/angular/primitives/**/*.js'),
        gulp.src('./public/js/**/*.js')
    )
        .pipe(concat('app.js'))
        .pipe(gulp.dest('./public/build/js'))
        .on('error', handleErrors);
});
dtothefp
la source
Voir aussi stream-series . Il ne vous oblige pas à spécifier le mode objet et fonctionne avec gulp-inject. Voyez ma réponse.
Codebling
il semble que la moitié des plugins gulp ne fonctionnent tout simplement pas de manière cohérente (comme l'ordre comme vous l'avez souligné), ce qui est vraiment dommage car le concept architectural de gulp est spectaculaire, juste tant de gens implémentent et maintiennent mal leurs plugins, je pense ... Je trouve que les modules de nœuds sous-jacents sont plus utiles, alors merci pour cette solution! Fonctionne très bien!
Jimmy Hoffa
1
streamqueue, event-stream n'a pas fonctionné pour moi, mais merge2 a fonctionné comme prévu npmjs.com/package/merge2
Alexander Shutau
12

Avec gulp-useref, vous pouvez concaténer chaque script déclaré dans votre fichier d'index, dans l'ordre dans lequel vous le déclarez.

https://www.npmjs.com/package/gulp-useref

var $ = require('gulp-load-plugins')();
gulp.task('jsbuild', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});
  return gulp.src('app/**/*.html')
    .pipe(assets)
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

Et dans le HTML, vous devez déclarer le nom du fichier de construction que vous souhaitez générer, comme ceci:

<!-- build:js js/main.min.js -->
    <script src="js/vendor/vendor.js"></script>
    <script src="js/modules/test.js"></script>
    <script src="js/main.js"></script>

Dans votre répertoire de construction, vous aurez la référence à main.min.js qui contiendra vendor.js, test.js et main.js

SuperIRis
la source
2
C'est parfait! J'ai détesté les réponses où j'avais besoin de définir l'ordre! Vous savez quoi? L'ordre est là: dans le fichier HTML. Solution parfaite.
Ali Ok
6

Le flux de tri peut également être utilisé pour garantir un ordre spécifique des fichiers avec gulp.src. Exemple de code qui place backbone.jstoujours comme dernier fichier à traiter:

var gulp = require('gulp');
var sort = require('sort-stream');
gulp.task('scripts', function() {
gulp.src(['./source/js/*.js', './source/js/**/*.js'])
  .pipe(sort(function(a, b){
    aScore = a.path.match(/backbone.js$/) ? 1 : 0;
    bScore = b.path.match(/backbone.js$/) ? 1 : 0;
    return aScore - bScore;
  }))
  .pipe(concat('script.js'))
  .pipe(stripDebug())
  .pipe(uglify())
  .pipe(gulp.dest('./build/js/'));
});
fracz
la source
Je souhaite que ce module fonctionne car cela me semble être la réponse la plus simple, mais dans mon cas, où j'ai numéroté les noms de fichiers et une fonction de comparaison très simple, cela ne fonctionne pas.
Jeremy John
5

J'ajoute simplement des nombres au début du nom de fichier:

0_normalize.scss
1_tikitaka.scss
main.scss

Cela fonctionne en gulp sans aucun problème.

user3360496
la source
1
Oui, je trouve cela un peu plus facile, je veux dire que si vous compilez tous vos fichiers pour la production, le nom que vous donnez à vos fichiers en développement ne fait aucune différence.
Michael Joseph Aubry
2
Je viens de découvrir que cela ne fonctionne pas correctement. essayez d'utiliser 1_xx, 2_xx, 10_xx, 11_xx. Sous Windows au moins, ce sera 1_xx, 10_xx, 11_xx, 2_xx
dbinott
17
Personnellement, je ne suis pas du tout d'accord avec l'affirmation selon laquelle le nom que vous donnez à vos fichiers en développement n'a pas d'importance. Tout développement doit d'abord être axé sur l'humain et non sur l'ordinateur. Changer vos fichiers pour qu'ils correspondent à votre outil de construction va à l'encontre de l'objectif d'un outil de construction. Modifiez votre construction pour qu'elle corresponde à votre projet, et non l'inverse.
Jon Hieb
2
@JonHieb D'une certaine manière, préfixer vos fichiers avec un nombre aidera également le prochain développeur à connaître les dépendances de chaque fichier, non? Si j'ouvre un dossier et que je vois 1_foo.js, 2_bar.js, 3_baz.js, j'ouvrirai ces fichiers dans cet ordre et lirai commencer à lire le code à 1_foo.js
sqram
J'ai trouvé que gulp.src fonctionne de manière asynchrone, ce qui signifie que cela ne fonctionne pas de manière cohérente dans les cas où il y a plus de fichiers à traiter dans un répertoire.
Jeremy John
5

J'ai mes scripts organisés dans différents dossiers pour chaque package que je tire de bower, plus mon propre script pour mon application. Puisque vous allez lister l'ordre de ces scripts quelque part, pourquoi ne pas simplement les lister dans votre fichier gulp? Pour les nouveaux développeurs sur votre projet, c'est bien que tous les points de terminaison de votre script soient répertoriés ici. Vous pouvez le faire avec gulp-add-src :

gulpfile.js

var gulp = require('gulp'),
    less = require('gulp-less'),
    minifyCSS = require('gulp-minify-css'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat'),
    addsrc = require('gulp-add-src'),
    sourcemaps = require('gulp-sourcemaps');

// CSS & Less
gulp.task('css', function(){
    gulp.src('less/all.less')
        .pipe(sourcemaps.init())
        .pipe(less())
        .pipe(minifyCSS())
        .pipe(sourcemaps.write('source-maps'))
        .pipe(gulp.dest('public/css'));
});

// JS
gulp.task('js', function() {
    gulp.src('resources/assets/bower/jquery/dist/jquery.js')
    .pipe(addsrc.append('resources/assets/bower/bootstrap/dist/js/bootstrap.js'))
    .pipe(addsrc.append('resources/assets/bower/blahblah/dist/js/blah.js'))
    .pipe(addsrc.append('resources/assets/js/my-script.js'))
    .pipe(sourcemaps.init())
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('source-maps'))
    .pipe(gulp.dest('public/js'));
});

gulp.task('default',['css','js']);

Remarque: jQuery et Bootstrap ont été ajoutés à des fins de démonstration de la commande. Il est probablement préférable d'utiliser les CDN pour ceux-ci, car ils sont si largement utilisés et les navigateurs pourraient déjà les mettre en cache à partir d'autres sites.

prograhammer
la source
3

Essayez stream-series . Cela fonctionne comme merge-stream / event-stream.merge () sauf qu'au lieu d'entrelacer, il s'ajoute à la fin. Il ne vous oblige pas à spécifier le mode objet comme streamqueue , donc votre code sort plus propre.

var series = require('stream-series');

gulp.task('minifyInOrder', function() {
    return series(gulp.src('vendor/*'),gulp.src('extra'),gulp.src('house/*'))
        .pipe(concat('a.js'))
        .pipe(uglify())
        .pipe(gulp.dest('dest'))
});
Codage
la source
2

merge2 ressemble au seul outil de fusion de flux ordonné fonctionnant et maintenu pour le moment.

Mise à jour 2020

Les API changent constamment, certaines bibliothèques deviennent inutilisables ou contiennent des vulnérabilités, ou leurs dépendances contiennent des vulnérabilités, qui ne sont pas corrigées depuis des années. Pour les manipulations de fichiers texte, vous feriez mieux d'utiliser des scripts NodeJS personnalisés et des bibliothèques populaires comme globbyet fs-extraavec d'autres bibliothèques sans wrappers Gulp, Grunt, etc.

import globby from 'globby';
import fs from 'fs-extra';

async function bundleScripts() {
    const rootPaths = await globby('./source/js/*.js');
    const otherPaths = (await globby('./source/**/*.js'))
        .filter(f => !rootFiles.includes(f));
    const paths = rootPaths.concat(otherPaths);

    const files = Promise.all(
        paths.map(
            // Returns a Promise
            path => fs.readFile(path, {encoding: 'utf8'})
        )
    );

    let bundle = files.join('\n');
    bundle = uglify(bundle);
    bundle = whatever(bundle);
    bundle = bundle.replace(/\/\*.*?\*\//g, '');

    await fs.outputFile('./build/js/script.js', bundle, {encoding: 'utf8'});
}

bundleScripts.then(() => console.log('done');
Alexander Shutau
la source
1

Une méthode alternative consiste à utiliser un plugin Gulp créé spécifiquement pour ce problème. https://www.npmjs.com/package/gulp-ng-module-sort

Il vous permet de trier vos scripts en ajoutant un en .pipe(ngModuleSort())tant que tel:

var ngModuleSort = require('gulp-ng-module-sort');
var concat = require('gulp-concat');

gulp.task('angular-scripts', function() {
    return gulp.src('./src/app/**/*.js')
        .pipe(ngModuleSort())
        .pipe(concat('angularAppScripts.js))
        .pipe(gulp.dest('./dist/));
});

En supposant une convention d'annuaire de:

|——— src/
|   |——— app/
|       |——— module1/
|           |——— sub-module1/
|               |——— sub-module1.js
|           |——— module1.js
|       |——— module2/
|           |——— sub-module2/
|               |——— sub-module2.js
|           |——— sub-module3/
|               |——— sub-module3.js
|           |——— module2.js
|   |——— app.js

J'espère que cela t'aides!

Chêne
la source
1

Pour moi, j'avais natualSort () et angularFileSort () dans pipe qui réorganisait les fichiers. Je l'ai enlevé et maintenant ça marche bien pour moi

$.inject( // app/**/*.js files
    gulp.src(paths.jsFiles)
      .pipe($.plumber()), // use plumber so watch can start despite js errors
      //.pipe($.naturalSort())
      //.pipe($.angularFilesort()),
    {relative: true}))
casper123
la source
1

J'utilise juste gulp-angular-filesort

function concatOrder() {

    return gulp.src('./build/src/app/**/*.js')
        .pipe(sort())
        .pipe(plug.concat('concat.js'))
        .pipe(gulp.dest('./output/'));
}
Maccurt
la source
oups, je viens de réaliser que vous ne
posiez
0

Je suis dans un environnement de module où tous dépendent du noyau en utilisant gulp. Alors lecore module doit être ajouté avant les autres.

Ce que j'ai fait:

  1. Déplacer tous les scripts dans un srcdossier
  2. Juste gulp-renamevotre coreannuaire pour_core
  3. gulp garde l'ordre de votre gulp.src, mon concat srcressemble à ceci:

    concat: ['./client/src/js/*.js', './client/src/js/**/*.js', './client/src/js/**/**/*.js']

Il prendra évidemment le _comme premier répertoire de la liste (tri naturel?).

Remarque (angularjs): J'utilise ensuite gulp-angular-extender pour ajouter dynamiquement les modules au coremodule. Compilé, il ressemble à ceci:

angular.module('Core', ["ui.router","mm.foundation",(...),"Admin","Products"])

Où Admin et Produits sont deux modules.

soyuka
la source
0

si vous souhaitez commander des dépendances de bibliothèques tierces, essayez wiredep . Ce paquet vérifie essentiellement chaque dépendance de paquet dans bower.json puis les connecte pour vous.

zak.http
la source
0

J'ai essayé plusieurs solutions de cette page, mais aucune n'a fonctionné. J'avais une série de fichiers numérotés que je voulais simplement classer par nom de dossier alphabétique, de sorte qu'une fois redirigés, concat()ils seraient dans le même ordre. Autrement dit, conservez l'ordre de l'entrée de globbing. Facile, non?

Voici mon code de preuve de concept spécifique ( printest juste pour voir la commande imprimée sur le cli):

var order = require('gulp-order');
var gulp = require('gulp');
var print = require('gulp-print').default;

var options = {};

options.rootPath = {
  inputDir: process.env.INIT_CWD + '/Draft',
  inputGlob: '/**/*.md',
};

gulp.task('default', function(){
  gulp.src(options.rootPath.inputDir + options.rootPath.inputGlob, {base: '.'})
    .pipe(order([options.rootPath.inputDir + options.rootPath.inputGlob]))
    .pipe(print());
});

La raison de la folie de gulp.src? J'ai déterminé qu'il gulp.srcfonctionnait de manière asynchrone lorsque j'ai pu utiliser une sleep()fonction (en utilisant un.map avec temps d'arrêt incrémenté par index) pour commander correctement la sortie du flux.

Le résultat de l'asynchronisation des srcrépertoires moyens avec plus de fichiers est venu après les répertoires avec moins de fichiers, car ils prenaient plus de temps à traiter.

Jeremy John
la source
1
Avez-vous trouvé une alternative synchrone (enfin, au moins déterministe)?
ELLIOTTCABLE
0

Dans ma configuration gulp, je spécifie d'abord les fichiers du fournisseur, puis je spécifie tout (plus général), ensuite. Et cela place avec succès le fournisseur js avant les autres éléments personnalisés.

gulp.src([
  // vendor folder first
  path.join(folder, '/vendor/**/*.js'),
  // custom js after vendor
  path.join(folder, '/**/*.js')
])    
ProGrammar
la source
0

Apparemment, vous pouvez passer l'option "nosort" à gulp.src gulp.src .

Marteau sauvage
la source