Concat scripts em ordem com Gulp


192

Digamos, por exemplo, que você esteja construindo um projeto no Backbone ou qualquer outra coisa e precise carregar scripts em uma determinada ordem, por exemplo, underscore.jsprecisa ser carregado antesbackbone.js .

Como faço para concatenar os scripts para que eles estejam em ordem?

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

Eu tenho a ordem correta dos scripts no meu source/index.htmlarquivo, mas como os arquivos são organizados por ordem alfabética, o gulp concatena underscore.jsdepois backbone.js, e a ordem dos scripts no meu source/index.htmlnão importa, ele examina os arquivos no diretório

Então, alguém tem uma idéia sobre isso?

Melhor ideia que eu tenho é renomear os scripts de fornecedores com 1, 2, 3para lhes dar a ordem correta, mas não tenho certeza se eu assim.

À medida que aprendi mais, descobri que o Browserify é uma ótima solução, pode ser uma dor no começo, mas é ótimo.


4
Eu devo mencionar que hoje em dia estou usando o browserify. Ele tem sua própria curva de aprendizado IMO. Eu lutei no começo, mas engolir o browserify é um caminho legal a seguir! Permitindo que seu código seja modular! Você lida com a ordem rapidamente, portanto, a concatenação não é necessária ao usar o browserify.
Michael Joseph Aubry

Gostaria de fornecer mais detalhes sobre sua solução ou um link?
Dmitri Zaitsev

kroltech.com/2013/12/… aqui está um link para um projeto padrão que realmente me ajudou a começar com um bom gerenciamento de projetos. Depois de sofrer com a aprendizagem de tudo isso, posso gerenciar meus projetos muito melhor. Ele tem o projeto no github e você pode ver como ele usa o browserify. Youtube sempre ajuda e, claro, a fonte em si é sempre subestimado github.com/substack/node-browserify#usage
Michael Joseph Aubry

Basicamente, a idéia é poder usar o npm como sintaxe requireno front-end, porque é claro que se você usou o npm no servidor, você vê como pode exigir módulos, mas o browserify permite fazer isso no código do cliente, mantenha em mente, para começar, é necessário um pouco de ajustes, mas está principalmente dentro do package.json e se você deseja usar com o gulp.js ou o grunt.js. Se você instalar o pacote gulp / grunt browserify, poderá executar gulp/grunt browserifye transformar seu script em um script principal, é uma pequena curva de aprendizado, mas vale a pena IMO.
9788 Michael Jackson Aubry

Obrigado! Na verdade, eu me deparei com ótimo artigo medium.com/@dickeyxxx/... fazendo um ponto bom que você realmente não precisa browserifyde Angularmódulos, onde obras de concatenação simples e ordem não importa :)
Dmitri Zaitsev

Respostas:


199

Recentemente, tive um problema semelhante com o Grunt ao criar meu aplicativo AngularJS. Aqui está uma pergunta que eu postei.

O que acabei fazendo é listar explicitamente os arquivos em ordem na configuração do grunhido. O arquivo de configuração ficará assim:

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

Grunt é capaz de descobrir quais arquivos são duplicados e não os inclui. A mesma técnica também funcionará com Gulp.


74
A propósito, isso funciona bem sob o gole também. Gulp também não repetirá os arquivos.
OverZealous

1
Caras legais, essas duas obras-primas são incríveis. Finalmente, configurei meu arquivo gulp.js para funcionar como eu queria, escrevi em algum html, salvei o arquivo e desenvolvi um site criado com as melhores estruturas e boas práticas com o toque de um botão. Além disso, as atualizações serão fáceis, se você não estiver usando nenhum deles, precisará!
Michael Joseph Aubry

1
Sim! Comecei a usar o Grunt recentemente, e é incrível. Chega de incorporar aplicativos JavaScript em estruturas de back-end.
Chad Johnson

3
O Gulp estava duplicando arquivos na minha tentativa, mas percebi que o caso era diferente entre gulp e o sistema de arquivos, então fique atento! No caso exato, o gulp não duplicará os arquivos.
Chris

2
O pedido manual é um pesadelo em um projeto sério. Existem classificadores de arquivos especiais - para angularjs e outros.
zhekaus

135

Outra coisa que ajuda se você precisar que alguns arquivos cheguem após um blob de arquivos é excluir arquivos específicos do seu globo, como:

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

Você pode combinar isso com a especificação de arquivos que precisam vir primeiro, como explicado na resposta de Chad Johnson.


Ah, na verdade, eu vi sua postagem mais cedo e isso me ajudou a injetar código na minha srce, na minha build, vi você usando essa sintaxe, acabei apagando essa parte porque não sabia exatamente por que precisava dela, agora faz sentido.
Michael Joseph Aubry

Oh ok, então o seu ponto aqui me atingiu, isso não faria o foobar.js durar? Não deveria ser o contrário. ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Michael Joseph Aubry

Sim, isso foi mais apenas uma ajuda adicional. É mais útil quando você precisa ou deseja que o código do aplicativo principal seja inserido depois que todo o resto for carregado. Não há razão para usá-lo ( !(foobar)) se você incluiu um arquivo específico anteriormente.
OverZealous

Para um aplicativo AngularJS em que minhas definições de módulo residem em arquivos que não possuem 'hífen' no nome, esse padrão Gulp glob funcionou; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Sam T

17

Eu usei o plug-in gulp-order, mas nem sempre é bem-sucedido, como você pode ver pelo meu módulo de nó de gulp-order overflow de estouro de pilha com fluxos mesclados . Ao navegar pelos documentos do Gulp, deparei-me com o módulo streamque, que funcionou muito bem para especificar a ordem da concatenação do meu caso.https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Exemplo de como eu usei está abaixo

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

Veja também stream-series . Não requer que você especifique o modo de objeto e funciona com gulp-inject. Veja minha resposta.
Codebling

parece que metade dos plugins gulp simplesmente não funciona de forma consistente (como a ordem que você apontou), o que é uma pena, porque o conceito arquitetônico de gulp é espetacular, tantas pessoas implementando e mantendo mal seus plugins, eu acho ... Acho que os módulos dos nós subjacentes são mais úteis, então obrigado por esta solução! Funciona bem!
Jimmy Hoffa

1
streamqueue, evento-stream não funcionou para mim, mas merge2 funcionou como esperado npmjs.com/package/merge2
Alexander Shutau

12

Com o gulp-useref, você pode concatenar todos os scripts declarados no seu arquivo de índice, na ordem em que o 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'}));
});

E no HTML você deve declarar o nome do arquivo de construção que deseja gerar, assim:

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

No diretório de construção, você terá a referência a main.min.js, que conterá vendor.js, test.js e main.js


2
Isto é perfeito! Eu odiava as respostas onde eu precisava definir a ordem! Você sabe o que? A ordem está lá: no arquivo HTML. Solução perfeita.
Ali Ok

6

O fluxo de classificação também pode ser usado para garantir a ordem específica dos arquivos gulp.src. Código de exemplo que coloca o backbone.jssempre como o último arquivo a ser processado:

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

Eu gostaria que este módulo funcionasse porque parece a resposta mais simples para mim, mas no meu caso, onde eu tenho numerados nomes de arquivos e uma função de comparação muito simples, isso não funciona.
Jeremy John

5

Acabei de adicionar números ao início do nome do arquivo:

0_normalize.scss
1_tikitaka.scss
main.scss

Funciona em gole sem problemas.


1
Sim, acho isso um pouco mais fácil, quero dizer, se você estiver compilando todos os seus arquivos para produção, não faz diferença o nome dos arquivos em desenvolvimento.
Michael Joseph Aubry

2
Acabei de descobrir que isso não funciona corretamente. tente usar 1_xx, 2_xx, 10_xx, 11_xx. No Windows, pelo menos, será 1_xx, 10_xx, 11_xx, 2_xx
dbinott

17
Pessoalmente, discordo totalmente da afirmação de que não importa o nome dos arquivos em desenvolvimento. Todo o desenvolvimento deve ser focado no ser humano primeiro, não no computador. Alterar seus arquivos para corresponder à sua ferramenta de construção anula o objetivo de uma ferramenta de construção. Mude sua construção para combinar com seu projeto, e não o contrário.
Jon Hieb

2
@JonHieb De certa forma, prefixar seus arquivos com um número também ajudará o próximo desenvolvedor a saber as dependências de cada arquivo, não? Se eu abrir uma pasta e vir 1_foo.js, 2_bar.js, 3_baz.js, vou abrir esses arquivos nessa ordem e ler começar a ler o código em 1_foo.js
sqram

Descobri que o gulp.src executa assíncrono, o que significa que isso não funciona de maneira consistente nos casos em que há mais arquivos para processar em um diretório.
Jeremy John

5

Tenho meus scripts organizados em pastas diferentes para cada pacote que recebo do caramanchão, além de meu próprio script para o meu aplicativo. Como você vai listar a ordem desses scripts em algum lugar, por que não apenas listá-los no seu arquivo gulp? Para novos desenvolvedores do seu projeto, é bom que todos os pontos de extremidade do script estejam listados aqui. Você pode fazer isso com o 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 adicionados para fins de demonstração de ordem. Provavelmente é melhor usar CDNs para aqueles, pois eles são tão amplamente usados ​​e os navegadores já podem armazená-los em cache de outros sites.


3

Tente stream-series . Funciona como merge-stream / event-stream.merge (), exceto que, em vez de intercalar, ele anexa ao final. Não requer que você especifique o modo de objeto como streamqueue , para que seu código saia mais limpo.

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

O merge2 parece a única ferramenta de fusão de fluxo ordenada em funcionamento e mantida no momento.

Atualização 2020

As APIs estão sempre mudando, algumas bibliotecas se tornam inutilizáveis ​​ou contêm vulnerabilidades, ou suas dependências contêm vulnerabilidades, que não são corrigidas por anos. Para manipulações de arquivos de texto, é melhor usar scripts personalizados do NodeJS e bibliotecas populares como globbye fs-extrajunto com outras bibliotecas sem os invólucros 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

Um método alternativo é usar um plug-in Gulp criado especificamente para esse problema. https://www.npmjs.com/package/gulp-ng-module-sort

Ele permite que você classifique seus scripts adicionando um .pipe(ngModuleSort())como:

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

Assumindo uma convenção de diretório 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 isto ajude!


1

Para mim, eu tinha natualSort () e angularFileSort () no pipe que estava reordenando os arquivos. Eu o removi e agora funciona bem para mim

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

Eu apenas uso gulp-angular-filesort

function concatOrder() {

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

oops, eu só percebi que você não estava perguntando sobre angular, desculpe
Maccurt

0

Estou em um ambiente de módulo em que todos são dependentes do núcleo usando gulp. Então ocore módulo precisa ser anexado antes dos outros.

O que eu fiz:

  1. Mova todos os scripts para uma srcpasta
  2. Apenas gulp-renameseu corediretório para_core
  3. gole está mantendo a ordem do seu gulp.src, minha concatsrc fica assim:

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

Obviamente, será _o primeiro diretório da lista (classificação natural?).

Nota (angularjs): Eu uso o gulp-angular-extender para adicionar dinamicamente os módulos aocore módulo. Compilado, fica assim:

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

Onde Admin e Produtos são dois módulos.


0

se você deseja solicitar dependências de bibliotecas de terceiros, tente wiredep . Este pacote basicamente verifica a dependência de cada pacote no bower.json e conecta-os a você.


0

Tentei várias soluções nesta página, mas nenhuma funcionou. Eu tinha uma série de arquivos numerados que eu simplesmente queria que fossem ordenados pelo nome da dobra em ordem alfabética concat(). Ou seja, preserve a ordem da entrada globbing. Fácil né?

Aqui está o meu código de prova de conceito específico ( printé apenas para ver a ordem impressa no 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());
});

A razão da loucura de gulp.src? Eu determinei que gulp.srcestava executando assíncrono quando consegui usar uma sleep()função (usando um.map com tempo de espera incrementado pelo índice) para ordenar a saída do fluxo corretamente.

O resultado da assíncrona dos srcdiretórios médios com mais arquivos veio depois dos diretórios com menos arquivos, porque eles demoraram mais para serem processados.


1
Você encontrou uma alternativa síncrona (bem, pelo menos determinística)?
ELLIOTTCABLE

0

Na minha configuração do gulp, estou especificando os arquivos do fornecedor primeiro e depois especificando tudo (mais geral) tudo, segundo. E coloca com êxito o fornecedor js antes dos outros itens personalizados.

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.