Concat-Skripte in der Reihenfolge mit Gulp

191

Angenommen, Sie underscore.jserstellen ein Projekt auf Backbone oder was auch immer und müssen Skripte in einer bestimmten Reihenfolge laden, z. B. müssen sie zuvor geladen werden backbone.js.

Wie kann ich die Skripte so zusammenfassen, dass sie in Ordnung sind?

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

Ich habe die richtige Reihenfolge der Skripte in meinem source/index.html, aber da die Dateien in alphabetischer Reihenfolge organisiert sind, konzentriert sich gulp underscore.jsdanach backbone.jsund die Reihenfolge der Skripte in meinem source/index.htmlspielt keine Rolle. Es werden die Dateien im Verzeichnis angezeigt .

Hat jemand eine Idee dazu?

Beste Idee , die ich habe , ist die Kreditoren Skripte umbenennen 1, 2, 3ihnen die richtige Reihenfolge zu geben, aber ich bin nicht sicher , ob ich so.

Als ich mehr erfuhr, fand ich, dass Browserify eine großartige Lösung ist. Es kann zunächst schmerzhaft sein, aber es ist großartig.

Michael Joseph Aubry
quelle
4
Ich könnte erwähnen, dass ich heutzutage browserify verwende. Es hat seine eigene kleine Lernkurve IMO. Ich hatte zuerst Probleme, aber schlucken browserify ist ein cooler Weg! Ermöglichen, dass Ihr Code modular aufgebaut ist! Sie bearbeiten die Bestellung in einem Shim, sodass bei Verwendung von browserify keine Verkettung erforderlich ist.
Michael Joseph Aubry
Möchten Sie weitere Details zu Ihrer Lösung oder einen Link angeben?
Dmitri Zaitsev
kroltech.com/2013/12/… hier ist ein Link zu einem Boilerplate-Projekt, das mir wirklich geholfen hat, mit einem guten Projektmanagement zu beginnen. Nachdem ich unter all dem gelitten habe, kann ich meine Projekte viel besser verwalten. Er hat das Projekt auf Github und Sie können sehen, wie er browserify verwendet. Youtube hilft immer und natürlich wird die Quelle selbst immer unterschätzt. Github.com/substack/node-browserify#usage
Michael Joseph Aubry
Grundsätzlich besteht die Idee darin, npm-ähnliche Syntax mit requireim Front-End zu verwenden, denn wenn Sie npm auf Ihrer Serverseite verwendet haben, sehen Sie natürlich, wie Sie Module benötigen können, aber browserify ermöglicht es Ihnen, dies auf dem clientseitigen Code zu tun Um loszulegen, muss man ein wenig basteln, aber es befindet sich hauptsächlich in package.json und wenn Sie es mit gulp.js oder grunt.js verwenden möchten. Wenn Sie das gulp / grunt browserify-Paket installieren, können Sie gulp/grunt browserifyIhr Skript ausführen und in ein Hauptskript verwandeln. Dies ist eine leichte Lernkurve, aber es lohnt sich IMO.
Michael Joseph Aubry
Vielen Dank! Eigentlich bin ich auf einen großartigen Artikel medium.com/@dickeyxxx/… gestoßen , der einen guten Punkt hervorhebt , den Sie browserifyfür AngularModule nicht wirklich benötigen , bei denen einfache Verkettung funktioniert und die Reihenfolge keine Rolle spielt :)
Dmitri Zaitsev

Antworten:

198

Ich hatte kürzlich ein ähnliches Problem mit Grunt beim Erstellen meiner AngularJS-App. Hier ist eine Frage, die ich gepostet habe.

Am Ende habe ich die Dateien explizit in der Reihenfolge in der Grunt-Konfiguration aufgelistet. Die Konfigurationsdatei sieht dann folgendermaßen aus:

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

Grunt kann herausfinden, welche Dateien Duplikate sind und diese nicht enthalten. Die gleiche Technik funktioniert auch mit Gulp.

Chad Johnson
quelle
74
Das funktioniert übrigens auch unter Schluck. Gulp wiederholt auch keine Dateien.
OverZealous
1
Coole Jungs, diese beiden Meisterwerke sind großartig. Ich habe gerade meine gulp.js-Datei so eingerichtet, dass sie wie gewünscht funktioniert, in HTML geschrieben, die Datei gespeichert und auf Knopfdruck eine Site mit den besten Frameworks und bewährten Methoden erstellt. Plus-Updates sind einfach, wenn Sie keines von beiden verwenden, das Sie benötigen!
Michael Joseph Aubry
1
Ja! Ich habe kürzlich angefangen, Grunt zu verwenden, und es ist großartig. Kein Einbetten von JavaScript-Anwendungen mehr in Backend-Frameworks.
Chad Johnson
3
Gulp hat bei meinem Versuch Dateien dupliziert, aber mir wurde klar, dass der Fall bei gulp anders war als beim Dateisystem. Achten Sie also darauf! Mit genauem Fall dupliziert gulp keine Dateien.
Chris
2
Manuelle Bestellung ist ein Albtraum in einem ernsthaften Projekt. Es gibt spezielle Dateisortierer - für Angularjs und andere.
Zhekaus
135

Eine andere Sache, die hilft, wenn Sie einige Dateien benötigen, um nach einem Blob von Dateien zu kommen , ist das Ausschließen bestimmter Dateien von Ihrem Glob, wie folgt:

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

Sie können dies mit der Angabe von Dateien kombinieren, die an erster Stelle stehen müssen, wie in Chad Johnsons Antwort erläutert.

Übereifrig
quelle
Ah, ich habe Ihren Beitrag tatsächlich früher gesehen und es hat mir geholfen, Code in meinen srcund meinen Code einzufügen. buildIch habe gesehen, wie Sie diese Syntax verwendet haben. Am Ende habe ich diesen Teil gelöscht, weil ich nicht genau wusste, warum ich ihn brauchte. Das macht jetzt Sinn.
Michael Joseph Aubry
Oh, okay, dein Punkt hier hat mich nur getroffen, würde das foobar.js nicht zum Letzten machen? Sollte es nicht umgekehrt sein? ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Michael Joseph Aubry
Ja, das war eher eine zusätzliche Hilfe. Dies ist am nützlichsten, wenn Sie Ihren Kernanwendungscode benötigen oder möchten, nachdem alles andere geladen wurde. Es gibt keinen Grund, es ( !(foobar)) zu verwenden, wenn Sie zuvor eine bestimmte Datei eingefügt haben.
OverZealous
Für eine AngularJS-Anwendung, in der sich meine Moduldefinitionen in Dateien befinden, deren Name "kein Bindestrich" enthält, hat dieses Gulp-Glob-Muster funktioniert. ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Sam T
17

Ich habe das Gulp-Order-Plugin verwendet, aber es ist nicht immer erfolgreich, wie Sie an meinem Stapelüberlauf nach dem Gulp-Order-Knotenmodul mit zusammengeführten Streams sehen können . Beim Durchsuchen der Gulp-Dokumente bin ich auf das Streamque-Modul gestoßen, das sich sehr gut für die Angabe der Reihenfolge der Verkettung in meinem Fall eignet. https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Ein Beispiel dafür, wie ich es benutzt habe, ist unten

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
quelle
Siehe auch Stream-Serien . Sie müssen den Objektmodus nicht angeben und arbeiten mit gulp-inj. Siehe meine Antwort.
Codebling
Es scheint, dass die Hälfte der gulp-Plugins einfach nicht konsistent funktioniert (wie die Reihenfolge, wie Sie betont haben), was eine Schande ist, weil das architektonische Konzept von gulp spektakulär ist, nur so viele Leute, die ihre Plugins schlecht implementieren und warten, denke ich ... Ich finde die zugrunde liegenden Knotenmodule nützlicher, also danke für diese Lösung! Funktioniert super!
Jimmy Hoffa
1
streamqueue, event-stream hat bei mir nicht funktioniert, aber merge2 hat wie erwartet funktioniert npmjs.com/package/merge2
Alexander Shutau
12

Mit gulp-useref können Sie jedes in Ihrer Indexdatei deklarierte Skript in der Reihenfolge verketten, in der Sie es deklarieren.

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

Und im HTML müssen Sie den Namen der Build-Datei, die Sie generieren möchten, wie folgt deklarieren:

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

In Ihrem Build-Verzeichnis befindet sich der Verweis auf main.min.js, der vendor.js, test.js und main.js enthält

SuperIRis
quelle
2
Dies ist perfekt! Ich hasste die Antworten, bei denen ich die Reihenfolge definieren musste! Weißt du was? Die Reihenfolge ist da: in der HTML-Datei. Perfekte Lösung.
Ali Ok
6

Der Sortierstrom kann auch verwendet werden, um eine bestimmte Reihenfolge der Dateien mit sicherzustellen gulp.src. Beispielcode, der die backbone.jsimmer als letzte zu verarbeitende Datei enthält:

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
quelle
Ich wünschte, dieses Modul würde funktionieren, weil es für mich die einfachste Antwort zu sein scheint, aber in meinem Fall, in dem ich Dateinamen und eine sehr einfache Vergleichsfunktion nummeriert habe, funktioniert dies nicht.
Jeremy John
5

Ich füge nur Zahlen am Anfang des Dateinamens hinzu:

0_normalize.scss
1_tikitaka.scss
main.scss

Es funktioniert in Schluck ohne Probleme.

user3360496
quelle
1
Ja, ich finde das ein bisschen einfacher. Ich meine, wenn Sie alle Ihre Dateien für die Produktion kompilieren, spielt es keine Rolle, wie Sie Ihre Dateien in der Entwicklung benennen.
Michael Joseph Aubry
2
Ich habe gerade herausgefunden, dass dies nicht richtig funktioniert. Versuchen Sie es mit 1_xx, 2_xx, 10_xx, 11_xx. Zumindest unter Windows wird es 1_xx, 10_xx, 11_xx, 2_xx sein
dbinott
17
Persönlich bin ich mit der Aussage, dass es egal ist, wie Sie Ihre Dateien in der Entwicklung benennen, ziemlich anderer Meinung. Jede Entwicklung sollte zuerst auf den Menschen ausgerichtet sein, nicht auf den Computer. Wenn Sie Ihre Dateien an Ihr Build-Tool anpassen, wird der Zweck eines Build-Tools zunichte gemacht. Ändern Sie Ihren Build so, dass er zu Ihrem Projekt passt, nicht umgekehrt.
Jon Hieb
2
@ JonHieb In gewisser Weise hilft das Präfixieren Ihrer Dateien mit einer Nummer auch dem nächsten Entwickler, die Abhängigkeiten jeder Datei zu kennen, nicht wahr? Wenn ich einen Ordner öffne und 1_foo.js, 2_bar.js, 3_baz.js sehe, öffne ich diese Dateien in dieser Reihenfolge und lese den Code zum Lesen unter 1_foo.js
sqram
Ich habe festgestellt, dass gulp.src asynchron ausgeführt wird, was bedeutet, dass dies in Fällen, in denen mehr Dateien in einem Verzeichnis verarbeitet werden müssen, nicht konsistent funktioniert.
Jeremy John
5

Ich habe meine Skripte in verschiedenen Ordnern für jedes Paket organisiert, das ich von bower abrufe, sowie mein eigenes Skript für meine App. Da Sie die Reihenfolge dieser Skripte irgendwo auflisten werden, warum nicht einfach in Ihrer gulp-Datei auflisten? Für neue Entwickler in Ihrem Projekt ist es schön, dass alle Ihre Skriptendpunkte hier aufgelistet sind. Sie können dies mit gulp-add-src tun :

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

Hinweis: jQuery und Bootstrap wurden zu Demonstrationszwecken der Bestellung hinzugefügt. Wahrscheinlich ist es besser, CDNs für diese zu verwenden, da sie so weit verbreitet sind und Browser sie möglicherweise bereits von anderen Websites zwischengespeichert haben.

Programmierer
quelle
3

Versuchen Sie es mit Stream-Serien . Es funktioniert wie merge-stream / event-stream.merge (), nur dass es nicht verschachtelt, sondern an das Ende angehängt wird. Sie müssen den Objektmodus nicht wie " Streamqueue" angeben , damit Ihr Code sauberer wird.

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

merge2 scheint derzeit das einzige funktionierende und gepflegte Tool zum Zusammenführen geordneter Streams zu sein.

Update 2020

Die APIs ändern sich ständig, einige Bibliotheken werden unbrauchbar oder enthalten Schwachstellen, oder ihre Abhängigkeiten enthalten Schwachstellen, die seit Jahren nicht mehr behoben sind. Für die Bearbeitung von Textdateien sollten Sie benutzerdefinierte NodeJS-Skripte und beliebte Bibliotheken wie globbyund fs-extrazusammen mit anderen Bibliotheken ohne Gulp-, Grunt- usw. Wrapper verwenden.

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
quelle
1

Eine alternative Methode ist die Verwendung eines Gulp-Plugins, das speziell für dieses Problem erstellt wurde. https://www.npmjs.com/package/gulp-ng-module-sort

Sie können Ihre Skripte sortieren, indem Sie Folgendes hinzufügen .pipe(ngModuleSort()):

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

Angenommen, eine Verzeichniskonvention von:

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

Hoffe das hilft!

Eiche
quelle
1

Für mich hatte ich natualSort () und angularFileSort () in Pipe, die die Dateien neu ordneten. Ich habe es entfernt und jetzt funktioniert es gut für mich

$.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
quelle
1

Ich benutze nur gulp-angle-filesort

function concatOrder() {

    return gulp.src('./build/src/app/**/*.js')
        .pipe(sort())
        .pipe(plug.concat('concat.js'))
        .pipe(gulp.dest('./output/'));
}
Maccurt
quelle
Hoppla, ich habe gerade gemerkt, dass Sie nicht nach eckigen Fragen gefragt haben, sorry
Maccurt
0

Ich bin in einer Modulumgebung, in der alle mit gulp kernabhängig sind. Also, diecore Modul muss also vor den anderen angehängt werden.

Was ich getan habe:

  1. Verschieben Sie alle Skripte in einen srcOrdner
  2. Nur gulp-renamedein coreVerzeichnis zu_core
  3. schluck hält die gulp.srcordnung von dir , mein concat srcsieht so aus:

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

Es wird offensichtlich das _als erstes Verzeichnis aus der Liste nehmen (natürliche Sortierung?).

Hinweis (anglejs): Ich verwende dann gulp- angle -extender , um die Module dynamisch zum coreModul hinzuzufügen . Zusammengestellt sieht es so aus:

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

Wobei Admin und Produkte zwei Module sind.

Sojuka
quelle
0

Wenn Sie Bibliotheksabhängigkeiten von Drittanbietern bestellen möchten , versuchen Sie es mit wiredep . Dieses Paket überprüft grundsätzlich jede Paketabhängigkeit in bower.json und verkabelt sie dann für Sie.

zak.http
quelle
0

Ich habe mehrere Lösungen von dieser Seite ausprobiert, aber keine hat funktioniert. Ich hatte eine Reihe von nummerierten Dateien, die ich einfach nach alphabetischem Ordnernamen sortieren wollte, damit concat()sie in der gleichen Reihenfolge weitergeleitet werden. Behalten Sie also die Reihenfolge der Globbing-Eingabe bei. Einfach richtig?

Hier ist mein spezifischer Proof-of-Concept-Code ( printnur um die auf dem CLI gedruckte Bestellung zu sehen):

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

Der Grund für den Wahnsinn von gulp.src? Ich habe festgestellt, dass gulp.srcasynchron ausgeführt wurde, als ich eine sleep()Funktion verwenden konnte (mithilfe von a.map mit dem Index inkrementierten Schlafzeit) verwenden konnte, um die Stream-Ausgabe ordnungsgemäß zu ordnen.

Das Ergebnis der Asynchronität von srcmittleren Verzeichnissen mit mehr Dateien kam nach Verzeichnissen mit weniger Dateien, da die Verarbeitung länger dauerte.

Jeremy John
quelle
1
Haben Sie eine synchrone (zumindest deterministische) Alternative gefunden?
ELLIOTTCABLE
0

In meinem Gulp-Setup spezifiziere ich zuerst die Herstellerdateien und dann das (allgemeinere) Alles. Und es stellt den Anbieter erfolgreich js vor die anderen benutzerdefinierten Sachen.

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