Guiones de Concat en orden con Gulp


192

Digamos, por ejemplo, que está creando un proyecto en Backbone o lo que sea y necesita cargar scripts en un orden determinado, por ejemplo underscore.js, debe cargarse antes backbone.js.

¿Cómo consigo que concatene los scripts para que estén en orden?

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

Tengo el orden correcto de los scripts en mi source/index.html, pero dado que los archivos están organizados por orden alfabético, gulp se concatenará underscore.jsdespués backbone.js, y el orden de los scripts en mi source/index.htmlno importa, mira los archivos en el directorio.

Entonces, ¿alguien tiene una idea sobre esto?

La mejor idea que tengo es cambiar el nombre de los proveedores con los scripts 1, 2, 3para darles el orden correcto, pero no estoy seguro de si me gusta este.

A medida que aprendí más, descubrí que Browserify es una gran solución, puede ser un dolor al principio, pero es genial.


44
Podría mencionar que hoy en día estoy usando browserify. Tiene su propia pequeña curva de aprendizaje IMO. Al principio me costó mucho, ¡pero tragar browserify es una buena manera de hacerlo! ¡Permitiendo que su código sea modular! Maneja el orden en una cuña, por lo que no es necesario concatenar cuando se usa el navegador
Michael Joseph Aubry

¿Le gustaría dar más detalles para su solución o un enlace?
Dmitri Zaitsev

kroltech.com/2013/12/… aquí hay un enlace a un proyecto repetitivo que realmente me ayudó a comenzar con una buena gestión de proyectos. Después de sufrir por aprender todo esto, puedo administrar mis proyectos mucho mejor. Tiene el proyecto en github y puedes ver cómo usa browserify. Youtube siempre ayuda y, por supuesto, la fuente en sí misma siempre se subestima github.com/substack/node-browserify#usage
Michael Joseph Aubry

Básicamente, la idea es poder usar npm como la sintaxis requireen la parte frontal porque, por supuesto, si ha usado npm en el lado del servidor, verá cómo puede requerir módulos, pero browserify le permite hacerlo en el código del lado del cliente, mantenga para comenzar, requiere un poco de retoques, pero está principalmente dentro de package.json y si quieres usarlo con gulp.js o grunt.js. Si instala el paquete gulp / grunt browserify puede ejecutar gulp/grunt browserifyy convertir su script en un script principal, es una ligera curva de aprendizaje pero vale la pena en mi opinión.
Michael Joseph Aubry

¡Gracias! De hecho, me encontré con gran artículo medium.com/@dickeyxxx/... haciendo un punto bueno que no necesita realmente browserifypara Angularmódulos, donde las obras de concatenación simples y el orden no importa :)
Dmitri Zaitsev

Respuestas:


199

Recientemente tuve un problema similar con Grunt al construir mi aplicación AngularJS. Aquí hay una pregunta que publiqué.

Lo que terminé haciendo es enumerar explícitamente los archivos en orden en la configuración de gruñido. El archivo de configuración se verá así:

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

Grunt es capaz de descubrir qué archivos son duplicados y no incluirlos. La misma técnica también funcionará con Gulp.


74
Esto funciona bien bajo trago también, por cierto. Gulp tampoco repetirá archivos.
OverZealous

1
Chicos geniales, estas dos obras maestras son increíbles. Finalmente, configuré mi archivo gulp.js para que funcione como quiero, escribí en algunos html, guardé el archivo y aumenté un sitio creado con los mejores marcos y buenas prácticas con solo tocar un botón. ¡Además, las actualizaciones serán fáciles, si no estás usando ninguna de las que necesitas!
Michael Joseph Aubry

1
¡Si! Empecé a usar Grunt recientemente, y es increíble. No más incrustación de aplicaciones JavaScript en marcos de back-end.
Chad Johnson

3
Gulp estaba duplicando archivos en mi intento, pero me di cuenta de que el caso era diferente en gulp frente al sistema de archivos, ¡así que ten cuidado! Con el caso exacto, gulp no duplicará archivos.
Chris

2
El pedido manual es una pesadilla en un proyecto serio. Hay clasificadores de archivos especiales, para angularjs y otros.
zhekaus

135

Otra cosa que ayuda si necesita que algunos archivos vengan después de una gran cantidad de archivos, es excluir archivos específicos de su globo, de esta manera:

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

Puede combinar esto con archivos de especificación que deben aparecer primero como se explica en la respuesta de Chad Johnson.


Ah, en realidad vi tu publicación antes y me ayudó a inyectar código en mi srcy buildte vi usando esa sintaxis, terminé borrando esa parte porque no estaba seguro exactamente por qué lo necesitaba, tiene sentido ahora.
Michael Joseph Aubry

Oh, está bien, entonces tu punto aquí solo me golpeó, ¿eso no haría que foobar.js dure? No debería ser al revés. ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Michael Joseph Aubry

Sí, esto fue más que un poco de ayuda adicional. Es más útil cuando necesita o desea que su código de aplicación principal entre después de que todo lo demás esté cargado. No hay razón para usarlo ( !(foobar)) si ha incluido un archivo específico de antemano.
OverZealous

Para una aplicación AngularJS donde las definiciones de mis módulos residen en archivos que no tienen 'guión' en el nombre, este patrón global de Gulp funcionó; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Sam T

17

He utilizado el complemento gulp-order pero no siempre es exitoso, como puede ver en mi módulo de nodo de desbordamiento posterior al orden de desbordamiento de pila con flujos combinados . Al navegar por los documentos de Gulp, me encontré con el módulo streamque que ha funcionado bastante bien para especificar el orden de la concatenación en mi caso. https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Ejemplo de cómo lo usé a continuación

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

Ver también stream-series . No requiere que especifique el modo de objeto, y funciona con gulp-inject. Mira mi respuesta.
Codebling

parece que la mitad de los complementos de Gulp simplemente no funcionan de manera consistente (como orden como usted señaló), lo cual es una lástima porque el concepto arquitectónico de Gulp es espectacular, creo que muchas personas implementan y mantienen sus complementos de manera deficiente. Creo que los módulos de nodo subyacentes son más útiles, ¡así que gracias por esta solución! ¡Funciona genial!
Jimmy Hoffa

1
streamqueue, event-stream no funcionó para mí, pero merge2 funcionó como se esperaba npmjs.com/package/merge2
Alexander Shutau

12

Con gulp-useref puede concatenar cada script declarado en su archivo de índice, en el orden en que lo declara.

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

Y en el HTML debe declarar el nombre del archivo de compilación que desea generar, así:

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

En su directorio de compilación tendrá la referencia a main.min.js que contendrá vendor.js, test.js y main.js


2
¡Esto es perfecto! ¡Odiaba las respuestas donde necesitaba definir el orden! ¿Sabes que? El orden está ahí: en el archivo HTML. Solución perfecta.
Ali Ok

6

La secuencia de clasificación también se puede utilizar para garantizar un orden específico de los archivos gulp.src. Código de muestra que coloca el backbone.jssiempre como el último archivo para procesar:

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

Desearía que este módulo funcionara porque parece la respuesta más simple para mí, pero en mi caso, donde tengo nombres de archivo numerados y una función de comparación muy simple, esto no funciona.
Jeremy John

5

Solo agrego números al comienzo del nombre del archivo:

0_normalize.scss
1_tikitaka.scss
main.scss

Funciona en trago sin ningún problema.


1
Sí, esto me resulta un poco más fácil, es decir, si está compilando todos sus archivos para producción, no importa cómo nombre sus archivos en desarrollo.
Michael Joseph Aubry

2
Me acabo de enterar que esto no funciona correctamente. intente usar 1_xx, 2_xx, 10_xx, 11_xx. Al menos en Windows, será 1_xx, 10_xx, 11_xx, 2_xx
dbinott el

17
Personalmente, estoy totalmente en desacuerdo con la afirmación de que no importa cómo nombren sus archivos en desarrollo. Todo desarrollo debe centrarse en el ser humano primero, no en la computadora. Cambiar sus archivos para que coincida con su herramienta de compilación anula el propósito de una herramienta de compilación. Cambie su compilación para que coincida con su proyecto, no al revés.
Jon Hieb

2
@ JonHieb De alguna manera, prefijar sus archivos con un número también ayudará al próximo desarrollador a conocer las dependencias de cada archivo, ¿no? Si abro una carpeta y veo 1_foo.js, 2_bar.js, 3_baz.js, abriré esos archivos en ese orden y leeré el código de lectura de inicio en 1_foo.js
sqram

He encontrado que gulp.src se ejecuta de forma asíncrona, lo que significa que esto no funciona de manera consistente en los casos en que hay más archivos para procesar en un directorio.
Jeremy John

5

Tengo mis scripts organizados en diferentes carpetas para cada paquete que extraigo de bower, además de mi propio script para mi aplicación. Dado que va a enumerar el orden de estos scripts en algún lugar, ¿por qué no simplemente enumerarlos en su archivo Gulp? Para los nuevos desarrolladores en su proyecto, es bueno que todos los puntos finales de su script estén listados aquí. Puedes hacer esto 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 y Bootstrap se agregaron con fines de demostración del pedido. Probablemente sea mejor usar CDN para aquellos, ya que son muy utilizados y los navegadores ya podrían tenerlos en caché de otros sitios.


3

Prueba la serie de transmisiones . Funciona como merge-stream / event-stream.merge () excepto que en lugar de intercalar, se agrega al final. No requiere que especifique el modo de objeto como streamqueue , por lo que su código sale más limpio.

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 parece ser la única herramienta de fusión de flujo ordenada en funcionamiento y mantenida en este momento.

Actualización 2020

Las API siempre cambian, algunas bibliotecas se vuelven inutilizables o contienen vulnerabilidades, o sus dependencias contienen vulnerabilidades que no se arreglan durante años. Para manipulaciones de archivos de texto, es mejor que use scripts personalizados de NodeJS y bibliotecas populares como globbyy fs-extrajunto con otras bibliotecas sin envoltorios 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');

1

Un método alternativo es usar un complemento Gulp creado específicamente para este problema. https://www.npmjs.com/package/gulp-ng-module-sort

Le permite ordenar sus scripts agregando un .pipe(ngModuleSort())como tal:

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

Asumiendo una convención de directorio 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

¡Espero que esto ayude!


1

Para mí tenía natualSort () y angularFileSort () en la tubería que estaba reordenando los archivos. Lo quité y ahora funciona bien para mí.

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

Solo uso gulp-angular-filesort

function concatOrder() {

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

Vaya, me he dado cuenta de que no estabas preguntando angular, lo siento
Maccurt

0

Estoy en un entorno de módulo donde todos son dependientes del núcleo que usan gulp. Entonces elcore módulo debe agregarse antes que los demás.

Lo que hice:

  1. Mueve todos los scripts a una srccarpeta
  2. Solo gulp-renametu coredirectorio para_core
  3. trago está manteniendo el orden de tu gulp.src, mi concat se srcve así:

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

Obviamente, tomará el _primer directorio de la lista (¿clasificación natural?).

Nota (angularjs): luego uso gulp-angular-extender para agregar dinámicamente los módulos al coremódulo. Compilado se ve así:

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

Donde Admin y Productos son dos módulos.


0

si desea solicitar dependencias de bibliotecas de terceros, intente wiredep . Este paquete básicamente verifica la dependencia de cada paquete en bower.json y luego los conecta por usted.


0

Probé varias soluciones desde esta página, pero ninguna funcionó. Tenía una serie de archivos numerados que simplemente quería ordenar por nombre de carpeta alfabético, por lo que cuando se canalizaba aconcat() estarían en el mismo orden. Es decir, preservar el orden de la entrada global. Fácil, verdad?

Aquí está mi código específico de prueba de concepto ( printes solo para ver el orden impreso en el 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 razón de la locura de gulp.src? Determiné que se gulp.srcestaba ejecutando de forma asincrónica cuando pude usar una sleep()función (usando un.map de sueño incrementado por índice) para ordenar la salida de la secuencia correctamente.

El resultado del asíncrono de los directorios srcmedios con más archivos vino después de los directorios con menos archivos, porque tomaron más tiempo procesarlos.


1
¿Encontraste una alternativa síncrona (bueno, al menos determinista)?
ELLIOTTCABLE

0

En mi configuración de gulp, primero estoy especificando los archivos del proveedor y luego especificando todo (más general), en segundo lugar. Y coloca con éxito al vendedor js antes que otras cosas personalizadas.

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.