Странное поведение grunt-contrib-concat при использовании шаблона подстановки

Я использую grunt, и я хотел бы объединить все файлы js в определенном каталоге в определенном порядке (это угловое приложение js, поэтому я хочу сначала выполнить определения своего модуля, а затем все остальное). Моя цель grunt concat выглядит так:

concat: {

    mobile: {
        expand: true,
        cwd: "static/javascript/mobile/app/",
        src: ["main-module.js", "**/*-module.js", "**/*.js", "!static/javascript/mobile/dist/*"],
        dest: "static/javascript/mobile/app/dist/ngmobile.concat.js"
    }
}

Приведенная выше конфигурация кажется похожей на то, что она должна объединять файл main-module.js, за которым следуют все остальные файлы module.js, а затем все остальное, опуская все в папке dist. Однако результат при запуске grunt с параметром --verbose выглядит примерно так:

Running "concat:mobile" (concat) task
Verifying property concat.mobile exists in config...OK
Files: static/javascript/mobile/app/main-module.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/main-module.js
Files: static/javascript/mobile/app/clients/clients-module.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/clients/clients-module.js
Files: static/javascript/mobile/app/reports/reports-module.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/reports/reports-module.js
Files: static/javascript/mobile/app/schedules/schedules-module.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/schedules/schedules-module.js
Files: static/javascript/mobile/app/services/services-module.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/services/services-module.js
Files: static/javascript/mobile/app/clients/addclient-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/clients/addclient-ctrl.js
Files: static/javascript/mobile/app/clients/clientprofile-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/clients/clientprofile-ctrl.js
Files: static/javascript/mobile/app/clients/clients-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/clients/clients-ctrl.js
Files: static/javascript/mobile/app/clients/clients-service.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/clients/clients-service.js
Files: static/javascript/mobile/app/common/directives.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/common/directives.js
Files: static/javascript/mobile/app/common/filters.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/common/filters.js
Files: static/javascript/mobile/app/common/header-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/common/header-ctrl.js
Files: static/javascript/mobile/app/common/navbarcollapse-directive.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/common/navbarcollapse-directive.js
Files: static/javascript/mobile/app/dist/ngmobile.concat.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/dist/ngmobile.concat.js
Files: static/javascript/mobile/app/main-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/main-ctrl.js
Files: static/javascript/mobile/app/main-service.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/main-service.js
Files: static/javascript/mobile/app/reports/reports-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/reports/reports-ctrl.js
Files: static/javascript/mobile/app/schedules/schedules-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/schedules/schedules-ctrl.js
Files: static/javascript/mobile/app/schedules/schedules-service.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/schedules/schedules-service.js
Files: static/javascript/mobile/app/services/addservice-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/services/addservice-ctrl.js
Files: static/javascript/mobile/app/services/serviceprofile-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/services/serviceprofile-ctrl.js
Files: static/javascript/mobile/app/services/services-ctrl.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/services/services-ctrl.js
Files: static/javascript/mobile/app/services/services-service.js -> static/javascript/mobile/app/dist/ngmobile.concat.js/services/services-service.js
Options: separator="\n", banner="", footer="", stripBanners=false, process=false
Reading static/javascript/mobile/app/main-module.js...OK
Writing static/javascript/mobile/app/dist/ngmobile.concat.js/main-module.js...ERROR
Warning: Unable to write "static/javascript/mobile/app/dist/ngmobile.concat.js/main-module.js" file (Error code: ENOTDIR). Use --force to continue.

Aborted due to warnings.

Это говорит мне, что он находит нужные мне файлы, а затем пытается записать (скопировать?) их в путь к файлу, указанный в dest. Как я понял это так ужасно неправильно? :) Если кто-то заботится о том, чтобы сохранить последние несколько волосков на моей голове, я был бы очень признателен за некоторые советы относительно того, что я здесь делаю неправильно. Я хочу объединить все файлы из src в файл dest.

Изменить

Если я удалю свойство expand, вывод будет выглядеть так:

Running "concat:mobile" (concat) task
Verifying property concat.mobile exists in config...OK
Files: main-module.js, clients/clients-module.js, reports/reports-module.js, schedules/schedules-module.js, services/services-module.js, clients/addclient-ctrl.js, clients/clientprofile-ctrl.js, clients/clients-ctrl.js, clients/clients-service.js, common/directives.js, common/filters.js, common/header-ctrl.js, common/navbarcollapse-directive.js, dist/ngmobile.concat.js, main-ctrl.js, main-service.js, reports/reports-ctrl.js, schedules/schedules-ctrl.js, schedules/schedules-service.js, services/addservice-ctrl.js, services/serviceprofile-ctrl.js, services/services-ctrl.js, services/services-service.js -> static/javascript/mobile/app/dist/ngmobile.concat.js
Options: separator="\n", banner="", footer="", stripBanners=false, process=false
>> Source file "main-module.js" not found.
>> Source file "clients/clients-module.js" not found.
>> Source file "reports/reports-module.js" not found.
>> Source file "schedules/schedules-module.js" not found.
>> Source file "services/services-module.js" not found.
>> Source file "clients/addclient-ctrl.js" not found.
>> Source file "clients/clientprofile-ctrl.js" not found.
>> Source file "clients/clients-ctrl.js" not found.
>> Source file "clients/clients-service.js" not found.
>> Source file "common/directives.js" not found.
>> Source file "common/filters.js" not found.
>> Source file "common/header-ctrl.js" not found.
>> Source file "common/navbarcollapse-directive.js" not found.
>> Source file "dist/ngmobile.concat.js" not found.
>> Source file "main-ctrl.js" not found.
>> Source file "main-service.js" not found.
>> Source file "reports/reports-ctrl.js" not found.
>> Source file "schedules/schedules-ctrl.js" not found.
>> Source file "schedules/schedules-service.js" not found.
>> Source file "services/addservice-ctrl.js" not found.
>> Source file "services/serviceprofile-ctrl.js" not found.
>> Source file "services/services-ctrl.js" not found.
>> Source file "services/services-service.js" not found.
Writing static/javascript/mobile/app/dist/ngmobile.concat.js...OK
File "static/javascript/mobile/app/dist/ngmobile.concat.js" created.

Обратите внимание, третья строка в вырезанном выше «Файлы: ...» перечисляет все файлы, которые я хотел найти, но затем говорит, что исходный файл не найден.

ИЗМЕНИТЬ 2

Приведенное ниже решение Мэтта решает проблему, ниже приведен мой обновленный код:

concat: {
    mobile: {
        dest: "static/javascript/mobile/app/dist/ngmobile-concat.js",
        src: (function () {
            var cwd = "static/javascript/mobile/app/";
            var files = ["*-module.js", "**/*-module.js", "**/*.js"];

            files = files.map(function (file) {
                return cwd + file;
            });

            files.push("! static/javascript/mobile/app/dist");

            return files;
        }())
    }
}

person Greg    schedule 01.02.2014    source источник


Ответы (2)


Столкнувшись с этим сам с угловым приложением, я рассмотрел три варианта:

1) Следуйте рекомендациям в ветке проекта на github

Вы не одиноки. На grunt-contrib-concat github есть длинный поток. проект по этому вопросу. Это связано с тем, как cwd работает с dest.

рекомендуемый обходной путь, размещенный там, перемещает свойство cwd в функция, которая затем выдает ожидаемые результаты, а не ошибку ENOTDIR.

src: (function() {
  var cwd = 'src/js/';
  var arr = [];
  // determine file order here and concat to arr
  return arr.map(function(file) { return cwd + file; });
}())

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

2) Откажитесь от cwd и используйте статический подход с ‹%= %> синтаксисом переменных.

Если ваши каталоги src и dest совпадают во всем файле grunt, вы можете определить каталоги src и dest следующим образом:

myapp: {
  // configurable paths
  app: "static/javascript/mobile/app/",
  dist: "static/javascript/mobile/app/dist/"
}

... затем вы можете удалить свойство cwd и настроить все так, чтобы оно выглядело более «статичным»:

concat: {

    mobile: {
        expand: true,
        src: ["<%= myapp.app %>/main-module.js", "<%= myapp.app %>/**/*-module.js", "<%= myapp.app %>/**/*.js", "!<%= myapp.dist %>/*"],
        dest: "<%= myapp.dist %>/ngmobile.concat.js"
    }
}

Для большинства моих проектов этого достаточно. Это просто, СУХО и легко понять, когда я вернусь позже и мне нужно будет внести изменения.

Предоставлено: свойства конфигурации поначалу становятся немного сложнее для чтения из-за всего ‹%=%> беспорядка, но с небольшим форматированием в моем редакторе, которое можно улучшить. Кроме того, другому программисту очень легко прочитать это и узнать, что вы имели в виду.

3) Используйте подключаемый модуль, например usemin, который сделает это за вас

Если вы также выполняете минификацию, вы можете рассмотреть плагин, например grunt-usemin.

Недостаток этого варианта очевиден: usemin добавляет сложности, потому что теперь у вас есть другой подключаемый модуль для управления и настройки, и вам нужно будет отформатировать свой HTML, чтобы использовать функциональность usemin. Конфигурация Usemin поначалу может сбивать с толку, и если вы работаете в команде, программист или дизайнер может очень легко сломать все, что нужно, чтобы изменить конфигурацию usemin или необходимые ему HTML-комментарии.

Положительным моментом является то, что usemin будет генерировать для вас конфигурации копирования grunt, concat и minification непосредственно из HTML. Таким образом, если вы добавите/удалите скрипты или измените порядок загрузки в HTML, вам не придется ничего делать в конфигурации grunt, потому что все это подхватит usemin.

person Matthew Bakaitis    schedule 01.02.2014
comment
Мэтт! Большое спасибо за помощь. У меня это работает сейчас, используя функцию самовызова anon, которая возвращает список файлов, используя подстановку. Я обновлю свой первоначальный вопрос точным кодом, но не могу отблагодарить вас за подробный ответ. - person Greg; 01.02.2014
comment
Спасибо за предложения. Я предпочитаю первый и пишу в одну строку: ['main-module.js', ...].map(function(file) {return 'src/js/'+file}) - person Fancyoung; 20.07.2014

Если установлено «cwd», grunt-contrib-concat просто копирует файлы, а не concat. Есть хитрое решение этой проблемы. Просто добавьте свойство «переименовать» и верните имя «назначение», как показано ниже.

rename: function (dest) {
              return dest;
        },
person burak    schedule 04.08.2016