Скрипты Concat в порядке с Gulp


192

Скажем, например, вы строите проект на Backbone или что-то еще, и вам нужно загружать скрипты в определенном порядке, например, underscore.jsнужно загружать раньше backbone.js.

Как мне заставить его согласовать сценарии, чтобы они были в порядке?

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

У меня есть правильный порядок сценариев в моем source/index.html, но, поскольку файлы организованы в алфавитном порядке, gulp будет объединяться underscore.jsпосле backbone.js, и порядок сценариев в моем source/index.htmlне имеет значения, он просматривает файлы в каталоге.

Так у кого-нибудь есть идеи по этому поводу?

Лучшая идея у меня есть переименовывать скрипты поставщика с 1, 2, 3чтобы дать им надлежащий порядок, но я не уверен , если я так.

Когда я узнал больше, я обнаружил, что Browserify - отличное решение, поначалу это может быть больно, но это здорово.


4
Я мог бы упомянуть, что в настоящее время я использую browserify. У него есть своя маленькая кривая обучения IMO. Сначала я боролся, но глоток просеянный - это крутой способ! Позволяя вашему коду быть модульным! Вы обрабатываете порядок в шимме, поэтому при использовании browserify объединение не требуется.
Майкл Джозеф Обри

Хотите дать более подробную информацию о вашем решении или ссылку?
Дмитрий Зайцев

kroltech.com/2013/12/… вот ссылка на шаблонный проект, который действительно помог мне начать хорошее управление проектами. Постигнув все это, я могу намного лучше управлять своими проектами. У него есть проект на github, и вы можете увидеть, как он использует browserify. Youtube всегда помогает и, конечно, сам источник всегда недооценивается. Github.com/substack/node-browserify#usage
Майкл Джозеф Обри

По сути, идея заключается в том, чтобы использовать интерфейс npm как во внешнем requireинтерфейсе, потому что, конечно, если вы использовали npm на стороне сервера, вы увидите, как вам могут потребоваться модули, но browserify позволяет вам делать это в коде на стороне клиента. чтобы начать, нужно немного поработать, но в основном он находится внутри package.json и если вы хотите использовать его с gulp.js или grunt.js. Если вы установите пакет gulp / grunt browserify, вы сможете запустить его gulp/grunt browserifyи превратить в один основной скрипт, это будет небольшой кривой обучения, но оно того стоит.
Майкл Джозеф Обри

Спасибо! Вообще -то я наткнулся на большую статью medium.com/@dickeyxxx/... делаю точку хорошо , что вы на самом деле не нужны browserifyдля Angularмодулей, где простые работы конкатенации и порядок не имеет значения :)
Дмитрий Зайцев

Ответы:


199

Недавно у меня была похожая проблема с Grunt при создании моего приложения AngularJS. Вот вопрос, который я отправил.

В итоге я явно перечислил файлы по порядку в конфиге grunt. Файл конфигурации будет выглядеть так:

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

Grunt может выяснить, какие файлы являются дубликатами, а не включать их. Та же самая техника будет работать и с Gulp.


74
Кстати, это прекрасно работает и при глотке. Gulp тоже не будет повторять файлы.
OverZealous

1
Крутые ребята, эти два шедевра просто потрясающие. Я просто наконец-то настроил свой файл gulp.js для работы так, как я хочу, написал в каком-то html-файле, сохранил файл и открыл сайт, созданный с использованием лучших фреймворков и хороших практик, одним нажатием кнопки. Плюс обновления будут простыми, если вы не используете ни одного, который вам нужен!
Майкл Джозеф Обри

1
Да! Я недавно начал использовать Grunt, и это здорово. Больше не нужно встраивать JavaScript-приложения в базовые фреймворки.
Чед Джонсон

3
Gulp дублировал файлы в моей попытке, но я понял, что дело в Gulp отличается от файловой системы, так что следите за этим! В точном случае, gulp не будет дублировать файлы.
Chris

2
Ручной заказ - это кошмар в серьезном проекте. Существуют специальные сортировщики файлов - для angularjs и других.
Жекаус

135

Еще одна вещь, которая помогает, если вам нужны некоторые файлы после большого количества файлов, - это исключение определенных файлов из вашего глобуса, например, так:

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

Вы можете объединить это с указанием файлов, которые должны быть на первом месте, как объяснено в ответе Чеда Джонсона.


Ах, я на самом деле видел ваш пост ранее, и это помогло мне внедрить код в мой srcи в мой, buildя видел, как вы использовали этот синтаксис, в итоге я удалил эту часть, потому что я не был точно уверен, зачем мне это нужно, имеет смысл сейчас.
Майкл Джозеф Обри

Ну ладно, твоя точка зрения меня просто поразила, разве это не сделает foobar.js последним? Разве это не должно быть наоборот. ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
Майкл Джозеф Обри

Да, это была просто дополнительная помощь. Это наиболее полезно, когда вам нужно или вы хотите, чтобы ваш основной код приложения появлялся после загрузки всего остального. Нет смысла использовать его ( !(foobar)), если вы предварительно включили определенный файл.
Чрезвычайный рев

Для приложения AngularJS, где определения моего модуля находятся в файлах, в имени которых нет тире, этот шаблон Gulp работает; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
Сэм Т

17

Я использовал плагин gulp-order, но он не всегда успешен, как вы можете видеть из моего модуля переполнения стека после модуля узла gulp-order с объединенными потоками . Просматривая документы Gulp, я наткнулся на модуль streamque, который довольно хорошо работал для определения порядка конкатенации в моем случае.https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

Пример того, как я использовал это ниже

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

Смотрите также поток-серия . Он не требует указывать режим объекта и работает с gulp-inject. Смотри мой ответ.
Кодирование

кажется, что половина плагинов gulp просто не работает согласованно (как порядок, как вы указали), что является плачевным позором, потому что архитектурная концепция gulp впечатляет, просто я думаю, что очень много людей плохо внедряют и поддерживают свои плагины ... Я считаю, что базовые модули узлов более полезны, так что спасибо за это решение! Прекрасно работает!
Джимми Хоффа

1
streamqueue, event-stream у меня не работал, но merge2 работал как положено npmjs.com/package/merge2
Александр Шутау

12

С помощью gulp-useref вы можете объединить каждый скрипт, объявленный в вашем индексном файле, в том порядке, в котором вы его объявили.

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

И в HTML вы должны объявить имя файла сборки, который вы хотите сгенерировать, например так:

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

В вашем каталоге сборки у вас будет ссылка на main.min.js, который будет содержать vendor.js, test.js и main.js


2
Это потрясающе! Я ненавидел ответы там, где мне нужно было определить порядок! Знаешь что? Порядок есть: в файле HTML. Идеальное решение.
Али Ок

6

Поток сортировки также может использоваться для обеспечения определенного порядка файлов с gulp.src. Пример кода, который помещает backbone.jsвсегда как последний файл для обработки:

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

Я хотел бы, чтобы этот модуль работал, потому что он кажется мне самым простым ответом, но в моем случае, когда я пронумеровал имена файлов и очень просто сравнил функцию, это не работает.
Джереми Джон

5

Я просто добавляю цифры в начало имени файла:

0_normalize.scss
1_tikitaka.scss
main.scss

Работает глотком без проблем.


1
Да, я нахожу это немного проще, я имею в виду, что если вы компилируете все свои файлы для производства, не имеет значения, как вы называете ваши файлы в процессе разработки.
Майкл Джозеф Обри

2
Я только что узнал, что это не работает правильно. попробуйте использовать 1_xx, 2_xx, 10_xx, 11_xx. Под Windows, по крайней мере, это будет 1_xx, 10_xx, 11_xx, 2_xx
dbinott

17
Лично я совершенно не согласен с утверждением, что не имеет значения, как вы называете ваши файлы в процессе разработки. Все развитие должно быть в первую очередь ориентировано на человека, а не на компьютер. Изменение ваших файлов в соответствии с вашим инструментом сборки отрицательно сказывается на назначении инструмента. Измените свою сборку, чтобы она соответствовала вашему проекту, а не наоборот.
Джон Хиб

2
@JonHieb В некотором смысле, добавление к файлам префикса также поможет следующему разработчику узнать зависимости каждого файла, не так ли? Если я открою папку и увижу 1_foo.js, 2_bar.js, 3_baz.js, я открою эти файлы в указанном порядке и прочту код начала чтения с 1_foo.js
квадрат

Я обнаружил, что gulp.src работает асинхронно, что означает, что это не работает согласованно в тех случаях, когда в каталоге есть больше файлов для обработки.
Джереми Джон

5

У меня есть свои сценарии, организованные в разные папки для каждого пакета, который я извлекаю из bower, плюс мой собственный сценарий для моего приложения. Поскольку вы собираетесь где-то перечислить порядок этих сценариев, почему бы просто не указать их в файле gulp? Для новых разработчиков в вашем проекте хорошо, что все конечные точки вашего скрипта перечислены здесь. Вы можете сделать это с помощью 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']);

Примечание: jQuery и Bootstrap добавлены для демонстрации порядка. Вероятно, лучше использовать CDN для них, поскольку они так широко используются, и браузеры уже могут кэшировать их с других сайтов.


3

Попробуйте потоковую серию . Он работает как merge-stream / event-stream.merge () за исключением того, что вместо перемежения он добавляется в конец. Вам не нужно указывать режим объекта , такой как streamqueue , поэтому ваш код получается чище.

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 выглядит как единственный работающий и поддерживаемый инструмент слияния упорядоченных потоков.

Обновление 2020

API постоянно меняются, некоторые библиотеки становятся непригодными или содержат уязвимости, или их зависимости содержат уязвимости, которые не исправляются годами. Для манипуляций с текстовыми файлами лучше использовать пользовательские скрипты NodeJS и популярные библиотеки, такие как globbyи fs-extraвместе с другими библиотеками без упаковщиков Gulp, Grunt и т. Д.

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

Альтернативный метод - использовать плагин Gulp, созданный специально для этой проблемы. https://www.npmjs.com/package/gulp-ng-module-sort

Это позволяет вам сортировать ваши сценарии, добавляя в качестве .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/));
});

Предполагая, что директория:

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

Надеюсь это поможет!


1

Для меня у меня были natualSort () и angularFileSort () в трубе, которая переупорядочивала файлы. Я удалил его, и теперь он отлично работает для меня

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

Я просто использую gulp-angular-filesort

function concatOrder() {

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

К сожалению, я только что понял, что вы не спрашиваете об угловых, извините
Maccurt

0

Я нахожусь в модульной среде, где все являются зависимыми от ядра, использующими gulp. Таким образом, coreмодуль должен быть добавлен перед другими.

Что я сделал:

  1. Переместить все скрипты в srcпапку
  2. Просто gulp-renameваш coreкаталог_core
  3. глоток следит за твоим порядком gulp.src, мой конкат srcвыглядит так:

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

Очевидно, он будет _первым каталогом из списка (естественная сортировка?).

Примечание (angularjs): Затем я использую gulp-angular-extender для динамического добавления модулей в coreмодуль. Скомпилировано это выглядит так:

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

Где Admin и Products - это два модуля.


0

если вы хотите заказать зависимости от сторонних библиотек, попробуйте wiredep . Этот пакет в основном проверяет каждую зависимость пакета в bower.json, а затем соединяет их для вас.


0

Я пробовал несколько решений с этой страницы, но ни одно не помогло. У меня была серия пронумерованных файлов, которые я просто хотел упорядочить по алфавитному имени, так что, когда я отправилconcat() они будут в том же порядке. То есть, сохранить порядок ввода ввода. Легко, правда?

Вот мой конкретный код для проверки концепции ( printпросто чтобы посмотреть заказ, напечатанный в 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());
});

Причина безумия gulp.src? Я определил, что gulp.srcработает асинхронно, когда я смог использоватьsleep() функцию (используя.map с временем ожидания, увеличенным на индекс), чтобы правильно упорядочить вывод потока.

Результатом асинхронности srcсредних папок с большим количеством файлов стал поиск папок с меньшим количеством файлов, потому что для их обработки потребовалось больше времени.


1
Вы нашли синхронную (ну, по крайней мере, детерминистическую) альтернативу?
ELLIOTTCABLE

0

В моей настройке gulp я сначала указываю файлы вендора, а затем указываю (более общее) все, во-вторых. И это успешно ставит поставщика JS перед другими пользовательскими вещами.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.