Yeoman: вызов подгенератора с пользовательскими аргументами

Я пишу свой первый генератор Yeoman, который запрашивает у пользователя различные входные данные и условно создает файлы на основе их ответов. Мне нужно иметь возможность вызывать подпрограмму (может быть подгенератором Yeoman) на основе пользовательского ввода и передавать ей аргументы.

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

Что я пробовал:

Я решил, что подгенераторы — это то, что нужно, поскольку я создаю наборы файлов только в том случае, если пользователь их запрашивает. Но у меня возникли проблемы с вызовом их условно и передачей им пользовательского ввода. Я пытался использовать hookFor, но получаю ошибку утверждения: hookFor must be used within the constructor only. (Поскольку я не хочу, чтобы он запускался по умолчанию, я вызываю подгенератор из моего this.prompt(prompts, function (props)).

Вопрос:

Как вызвать подпрограмму только в том случае, если пользователь запрашивает ее (через приглашение) и передать этой подпрограмме некоторую информацию, предоставленную пользователем?

Если вы любезны ответить, пожалуйста, не думайте, что я пробовал что-то очевидное ;-).


person Fiona    schedule 21.12.2013    source источник


Ответы (3)


Предположим, у вас есть генератор generator-blog (BlogGenerator) с двумя вспомогательными генераторами (блог-сервер и блог-клиент):

app\index.js
client\index.js
server\index.js

Итак, когда вы запускаете yo blog, вам нужно спросить пользователя о некоторых параметрах и запустить (необязательно) подгенераторы, верно?

Чтобы запустить подгенератор, вам нужно вызвать this.invoke("generator_namespace", {options: {}}). Второй переданный нами аргумент может иметь поле options — это объект опций, который будет передан генератору.

В приложении\index.js:

BlogGenerator.prototype.askFor = function askFor() {
  var cb = this.async();

  // have Yeoman greet the user.
  console.log(this.yeoman);

  var prompts = [{
    name: 'appName',
    message: 'Enter your app name',
    default: 'MyBlog'
  }, {
    type: 'confirm',
    name: 'createServer',
    message: 'Would you like to create server project?',
    default: true
  }, {
    type: 'confirm',
    name: 'createClient',
    message: 'Whould you like to create client project?',
    default: true
  }];

  this.prompt(prompts, function (props) {
    this.appName = props.appName;
    this.createServer = props.createServer;
    this.createClient = props.createClient;

    cb();
  }.bind(this));
}

BlogGenerator.prototype.main = function app() {
  if (this.createClient) {
    // Here: we'are calling the nested generator (via 'invoke' with options)
    this.invoke("blog:client", {options: {nested: true, appName: this.appName}});
  }
  if (this.createServer) {
    this.invoke("blog:server", {options: {nested: true, appName: this.appName}});
  }
};

В клиенте\index.js:

var BlogGenerator = module.exports = function BlogGenerator(args, options, config) {
  var that = this;
  yeoman.Base.apply(this, arguments);
  // in this.options we have the object passed to 'invoke' in app/index.js:
  this.appName = that.options.appName;
  this.nested  = that.options.nested;
};

BlogGenerator .prototype.askFor = function askFor() {
  var cb = this.async();

  if (!this.options.nested) {
    console.log(this.yeoman);
  }
}

ОБНОВЛЕНИЕ 21 декабря 2015 г..
Использование invoke больше не рекомендуется и должно быть заменено на composeWith. Но это не так просто, как могло бы быть. Основное отличие invoke от composeWith в том, что теперь у вас нет возможности управлять подгенераторами. Вы могли только объявить их использование.
Вот как должен выглядеть метод main сверху:

BlogGenerator.prototype.main = function app() {
  if (this.createClient) {
    this.composeWith("blog:client", { 
        options: { 
          nested: true, 
          appName: this.appName
        } 
      }, {
        local: require.resolve("./../client")
      });
  }
  if (this.createServer) {
    this.composeWith("blog:server", { 
        options: { 
          nested: true, 
          appName: this.appName
        } 
      }, {
        local: require.resolve("./../server")
      });
  }
};

Также я удалил, заменил yeoman.generators.Base на yeoman.Base.

person Shrike    schedule 27.12.2013
comment
Именно то, что я искал. Спасибо. - person Fiona; 04.01.2014
comment
Я знаю, что это старо, но это близко к тому, что я ищу. Моя проблема в том, что мой подгенератор расширяет yeoman.generators.NamedBase. Когда я запускаю этот код, я получаю сообщение об ошибке: не указано требуемое имя аргумента! Я пытаюсь выяснить, как также передать имя (т.е. тест в тесте yo make:controller) подгенераторам, вызываемым .invoke(). Любая помощь? - person ItJustWerks; 10.02.2015
comment
Понятно: this.invoke(make:controller, {options: {nested: true, ...}, args: [this.name] }); - person ItJustWerks; 10.02.2015
comment
если вы получаете эту ошибку Не указали требуемое имя аргумента yeoman -- с помощью composeWith. Просто измените yeoman.generators.NamedBase на yeoman.generators.Base github.com/yeoman/generator/issues /521 - person keithics; 21.12.2015

Обновление 2015-04: API-интерфейс yoman теперь включает this.composeWith в качестве предпочтительного метода для связывания генераторов.

документы: http://yeoman.io/authoring/composability.html

person TomFuertes    schedule 12.04.2015

Вы можете охватить все возможные сценарии выполнения, проверку условий, подсказки при составлении генераторов вместе, если вы разделяете генераторы и используете «основной генератор», вам поможет цикл контекста запуска. Используйте options из .composeWith('my-genertor', { 'options' : options }) для передачи конфигураций составным генераторам.

При использовании .composeWith функция группы приоритетов (например: prompting, writing...) будет выполняться для всех генераторов, затем будет выполняться следующая группа приоритетов. Если вы вызываете .composeWith для generatorB из generatorA, тогда выполнение будет, например:

генераторA.prompting => генераторB.prompting => генераторA.запись => генераторB.запись

Если вы хотите контролировать выполнение между разными генераторами, я советую вам создать «основной» генератор, который объединяет их вместе, как написано в http://yeoman.io/authoring/composability.html#order:

// In my-generator/generators/turbo/index.js
module.exports = require('yeoman-generator').Base.extend({
  'prompting' : function () {
    console.log('prompting - turbo');
  },

  'writing' : function () {
    console.log('prompting - turbo');
  }
});

// In my-generator/generators/electric/index.js
module.exports = require('yeoman-generator').Base.extend({
  'prompting' : function () {
    console.log('prompting - zap');
  },

  'writing' : function () {
    console.log('writing - zap');
  }
});

// In my-generator/generators/app/index.js
module.exports = require('yeoman-generator').Base.extend({
  'initializing' : function () {
    this.composeWith('my-generator:turbo');
    this.composeWith('my-generator:electric');
  }
});
person p1100i    schedule 01.03.2016