Concatena gli script in ordine con Gulp


192

Supponiamo, ad esempio, che stai costruendo un progetto su Backbone o qualsiasi altra cosa e che devi caricare gli script in un determinato ordine, ad esempio, devi underscore.jsprima caricarli backbone.js.

Come ottengo per concatenare gli script in modo che siano in ordine?

// 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/'));
});

Ho il giusto ordine di script nel mio source/index.html, ma dal momento che i file sono organizzati in ordine alfabetico, gulp si concatenerà underscore.jsdopo backbone.jse l'ordine degli script nel mio source/index.htmlnon ha importanza, guarda i file nella directory.

Qualcuno ha un'idea su questo?

Migliore idea che ho è di rinominare gli script vendor con 1, 2, 3per dare loro il giusto ordine, ma non sono sicuro se mi piace questo.

Come ho imparato di più ho scoperto che Browserify è un'ottima soluzione, all'inizio può essere una seccatura ma è grandiosa.


4
Potrei menzionare che adesso sto usando browserify. Ha la sua piccola curva di apprendimento IMO. All'inizio ho faticato ma gulp browserify è un bel modo di procedere! Consentire al tuo codice di essere modulare! Gestisci l'ordine in uno shim, quindi non è necessario concatenare quando si utilizza browserify.
Michael Joseph Aubry,

Vuoi fornire maggiori dettagli per la tua soluzione o un link?
Dmitri Zaitsev,

kroltech.com/2013/12/… ecco un link a un progetto di boilerplate che mi ha davvero aiutato a iniziare con una buona gestione del progetto. Dopo aver sofferto con l'apprendimento di tutto ciò, posso gestire i miei progetti molto meglio. Ha il progetto su github e puoi vedere come usa browserify. Youtube aiuta sempre e ovviamente la fonte stessa è sempre sottovalutata github.com/substack/node-browserify#usage
Michael Joseph Aubry

Fondamentalmente l'idea è quella di essere in grado di usare npm come sintassi con requireil front-end perché ovviamente se hai usato npm sul tuo server vedi come puoi richiedere moduli, ma browserify ti consente di farlo sul codice lato client, mantieni in mente per iniziare richiede un po 'di armeggiamento, ma è principalmente all'interno di package.json e se si desidera utilizzare con gulp.js o grunt.js. Se installi il pacchetto browserify gulp / grunt che puoi eseguire gulp/grunt browserifye trasformare il tuo script in uno script principale, è una leggera curva di apprendimento ma ne vale la pena IMO.
Michael Joseph Aubry,

Grazie! In realtà mi sono imbattuto in un ottimo articolo medium.com/@dickeyxxx/… sottolineando che non è davvero necessario browserifyper i Angularmoduli, dove la concatenazione semplice funziona e l'ordine non importa :)
Dmitri Zaitsev

Risposte:


199

Recentemente ho avuto un problema simile con Grunt durante la creazione della mia app AngularJS. Ecco una domanda che ho pubblicato.

Quello che ho finito è elencare esplicitamente i file in ordine nella configurazione grunt. Il file di configurazione apparirà così:

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

Grunt è in grado di capire quali file sono duplicati e non includerli. La stessa tecnica funzionerà anche con Gulp.


74
A proposito, questo funziona bene anche con il gulp. Gulp non ripeterà neanche i file.
OverZealous,

1
Fantastici ragazzi, questi due capolavori sono fantastici. Ho appena impostato il mio file gulp.js in modo che funzioni come voglio, ho scritto in html, salvato il file e potenziato un sito creato con i migliori framework e buone pratiche con il semplice tocco di un pulsante. Inoltre, gli aggiornamenti saranno facili, se non ne usi uno, devi farlo!
Michael Joseph Aubry,

1
Sì! Ho iniziato a usare Grunt di recente, ed è fantastico. Non è più necessario incorporare applicazioni JavaScript all'interno di framework back-end.
Chad Johnson,

3
Gulp stava duplicando i file nel mio tentativo, ma mi sono reso conto di avere il caso diverso in gulp rispetto al file system, quindi fai attenzione! Nel caso esatto, gulp non duplicherà i file.
Chris,

2
L'ordinamento manuale è un incubo in un progetto serio. Esistono selezionatori di file speciali - per angularjs e altri.
Zhekaus,

135

Un'altra cosa che aiuta se hai bisogno di alcuni file per venire dopo un blob di file, è di escludere file specifici dal tuo glob, in questo modo:

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

Puoi combinare questo con specificare i file che devono venire prima come spiegato nella risposta di Chad Johnson.


Ah, in realtà ho visto il tuo post in precedenza e mi ha aiutato a iniettare codice nel mio srce nel mio buildti ho visto usare quella sintassi, ho finito per cancellare quella parte perché non ero sicuro del perché esattamente ne avevo bisogno, ora ha senso.
Michael Joseph Aubry,

Oh okay, quindi il tuo punto qui mi ha colpito, non sarebbe che durare foobar.js? Non dovrebbe essere il contrario. ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Michael Joseph Aubry,

Sì, questo era più solo un ulteriore aiuto. È molto utile quando è necessario o si desidera che il codice dell'applicazione principale arrivi dopo aver caricato tutto il resto. Non c'è motivo di usarlo ( !(foobar)) se in precedenza hai incluso un file specifico.
OverZealous,

Per un'applicazione AngularJS in cui le definizioni del mio modulo risiedono in file che non hanno "nessun trattino" nel nome, questo modello glob di Gulp ha funzionato; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Sam T

17

Ho usato il plugin gulp-order ma non ha sempre successo come potete vedere dal mio modulo nodo nodo gulp-order post overflow dello stack con flussi uniti . Durante la navigazione tra i documenti di Gulp mi sono imbattuto nel modulo streamque che ha funzionato abbastanza bene per specificare l'ordine nel mio caso di concatenazione. https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Di seguito è riportato un esempio di come l'ho usato

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);
});

Vedi anche stream-series . Non richiede di specificare la modalità oggetto e funziona con gulp-inject. Vedi la mia risposta
Codebling

sembra che metà dei plugin di gulp semplicemente non funzionino in modo coerente (come l'ordine come hai sottolineato), il che è un peccato pianto perché il concetto architettonico di gulp è spettacolare, solo così tante persone che implementano e mantengono male i loro plugin penso ... Trovo che i moduli dei nodi sottostanti siano più utili, quindi grazie per questa soluzione! Funziona alla grande!
Jimmy Hoffa,

1
streamqueue, event-stream non ha funzionato per me, ma merge2 ha funzionato come previsto npmjs.com/package/merge2
Alexander Shutau,

12

Con gulp-useref puoi concatenare ogni script dichiarato nel tuo file indice, nell'ordine in cui lo dichiari.

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'}));
});

E nell'HTML devi dichiarare il nome del file di build che vuoi generare, in questo modo:

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

Nella tua directory di build avrai il riferimento a main.min.js che conterrà vendor.js, test.js e main.js


2
Questo è perfetto! Odiavo le risposte dove avevo bisogno di definire l'ordine! Sai cosa? L'ordine è lì: nel file HTML. Soluzione perfetta
Ali Ok,

6

Il flusso di ordinamento può anche essere utilizzato per garantire l'ordine specifico dei file con gulp.src. Codice di esempio che inserisce backbone.jssempre come ultimo file da elaborare:

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/'));
});

Vorrei che questo modulo funzionasse perché mi sembra la risposta più semplice, ma nel mio caso, dove ho nomi di file numerati e una funzione di confronto molto semplice, questo non funziona.
Jeremy John,

5

Aggiungo solo numeri all'inizio del nome del file:

0_normalize.scss
1_tikitaka.scss
main.scss

Funziona a gulp senza problemi.


1
Sì, lo trovo un po 'più semplice, intendo che se stai compilando tutti i tuoi file per la produzione non fa alcuna differenza ciò che dai un nome ai tuoi file in fase di sviluppo.
Michael Joseph Aubry,

2
Ho appena scoperto che non funziona correttamente. prova a usare 1_xx, 2_xx, 10_xx, 11_xx. Almeno sotto Windows, saranno 1_xx, 10_xx, 11_xx, 2_xx
dbinott

17
Personalmente, non sono assolutamente d'accordo con l'affermazione secondo cui non importa come si chiamano i file in fase di sviluppo. Tutto lo sviluppo dovrebbe prima essere incentrato sull'uomo, non incentrato sul computer. La modifica dei file in modo che corrisponda allo strumento di compilazione vanifica lo scopo di uno strumento di compilazione. Cambia la tua build in modo che corrisponda al tuo progetto, non viceversa.
Jon Hieb,

2
@JonHieb In un certo senso, il prefisso dei file con un numero aiuterà anche lo sviluppatore successivo a conoscere le dipendenze di ciascun file, no? Se apro una cartella e visualizzo 1_foo.js, 2_bar.js, 3_baz.js, aprirò quei file in questo ordine e leggerò il codice di inizio lettura su 1_foo.js
sqram

Ho scoperto che gulp.src funziona in modo asincrono, il che significa che non funziona in modo coerente nei casi in cui ci sono più file da elaborare in una directory.
Jeremy John,

5

Ho i miei script organizzati in cartelle diverse per ogni pacchetto che estraggo da Bower, oltre al mio script per la mia app. Poiché hai intenzione di elencare l'ordine di questi script da qualche parte, perché non elencarli nel tuo file gulp? Per i nuovi sviluppatori del tuo progetto, è bello che tutti gli endpoint degli script siano elencati qui. Puoi farlo con 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']);

Nota: jQuery e Bootstrap aggiunti a scopo dimostrativo dell'ordine. Probabilmente è meglio usare i CDN per quelli poiché sono così ampiamente utilizzati e i browser potrebbero averli già memorizzati nella cache da altri siti.


3

Prova stream-series . Funziona come merge-stream / event-stream.merge () tranne che invece di interfogliare, si aggiunge alla fine. Non richiede di specificare la modalità oggetto come streamqueue , quindi il codice risulta più pulito.

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'))
});

2

merge2 sembra attualmente l'unico strumento di fusione del flusso ordinato funzionante e gestito.

Aggiornamento 2020

Le API cambiano sempre, alcune librerie diventano inutilizzabili o contengono vulnerabilità o le loro dipendenze contengono vulnerabilità che non vengono risolte per anni. Per manipolazioni di file di testo è meglio usare script NodeJS personalizzati e librerie popolari come globbye fs-extrainsieme ad altre librerie senza involucri Gulp, Grunt, ecc.

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');

1

Un metodo alternativo consiste nell'utilizzare un plug-in Gulp creato appositamente per questo problema. https://www.npmjs.com/package/gulp-ng-module-sort

Ti consente di ordinare i tuoi script aggiungendo un .pipe(ngModuleSort())come tale:

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/));
});

Supponendo una convenzione di directory di:

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

Spero che questo ti aiuti!


1

Per me avevo natualSort () e angularFileSort () in pipe che stava riordinando i file. L'ho rimosso e ora funziona bene per me

$.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}))

1

Uso solo gulp-angular-filesort

function concatOrder() {

    return gulp.src('./build/src/app/**/*.js')
        .pipe(sort())
        .pipe(plug.concat('concat.js'))
        .pipe(gulp.dest('./output/'));
}

oops, ho appena realizzato che non stavi chiedendo di angolare, scusa
Maccurt

0

Sono in un ambiente di modulo in cui tutti dipendono dal core usando gulp. Pertanto, il coremodulo deve essere aggiunto prima degli altri.

Cosa ho fatto:

  1. Sposta tutti gli script in una srccartella
  2. Solo la gulp-renametua coredirectory per_core
  3. gulp sta mantenendo l'ordine del tuo gulp.src, il mio concat si srcpresenta così:

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

Ovviamente prenderà _come prima directory dall'elenco (ordinamento naturale?).

Nota (angularjs): utilizzo quindi gulp- angular -extender per aggiungere dinamicamente i moduli al coremodulo. Compilato sembra così:

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

Dove Admin e prodotti sono due moduli.


0

se desideri ordinare dipendenze da librerie di terze parti, prova cablato . Questo pacchetto controlla sostanzialmente ogni dipendenza del pacchetto in bower.json e poi li collega.


0

Ho provato diverse soluzioni da questa pagina, ma nessuna ha funzionato. Avevo una serie di file numerati che volevo semplicemente essere ordinati in base al nome utente alfabetico, quindi quando venivano convogliati concat(), sarebbero stati nello stesso ordine. Cioè, preservare l'ordine dell'input globbing. Facile vero?

Ecco il mio specifico codice di prova ( printè solo per vedere l'ordine stampato sul 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());
});

Il motivo della follia di gulp.src? Ho determinato che gulp.srcera in esecuzione asincrona quando sono stato in grado di utilizzare una sleep()funzione (utilizzando a .mapcon sleeptime incrementato dall'indice) per ordinare correttamente l'output del flusso.

Il risultato dell'asincronizzazione delle srcdirectory medie con più file è arrivato dopo le directory con un numero inferiore di file, perché hanno impiegato più tempo per l'elaborazione.


1
Hai trovato un'alternativa sincrona (bene, almeno deterministica)?
ELLIOTTCABLE

0

Nella mia configurazione di gulp, sto prima specificando i file del fornitore e poi specificando tutto (più generale), secondo. E mette con successo il fornitore js prima delle altre cose personalizzate.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.