Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yeoman generator add a new file generated exsiting project

I've yeoman generator which generate a simple sproject successfully.

I want that after the project generation, in latter time that the use will have the ability to generate a new file deployment.yaml under the app folder, however it needs to read some data from the main generator for example appName as the sub-generator needs to generate a new file inside the generated application.

e.g. yo tdk

This command generates a new project

And when I run yo tdk:event (or something similar) it will generate a new file inside the project app folder

For illustration I've created this very simple generator

const Generator = require("yeoman-generator");

module.exports = class extends Generator {
  prompting() {
    this.props = {
      appName: "my-app",
      srvName: "my-service"
    };

    const prompts = [
      {
        name: "appName",
        message: "Project name: ",
        type: "input",
        default: this.props.appName
      },
      {
        name: "srvName",
        message: "Service name: ",
        type: "input",
        default: this.props.srvName
      }
    ];

    return this.prompt(prompts).then(props => {
      this.props = props;
    });
  }

  writing() {
    this.fs.copyTpl(
      this.templatePath("app"),
      this.destinationPath(this.props.appName),
      this.props
    );
  }
};

This generator have two simple question

  1. app name
  2. service name

And it will generate a project like

myapp   /root
 -app   /folder
  - service.yaml   /single file at the project generation

The generated service.yaml looks like following:

apiVersion: v1
kind: Service
metadata:
  name: <%= appName %>
spec:
  selector:
    app: <%= srvName %>
  ports:
    - protocol: TCP
      port: 80

Now after the generation of the project with this service.yaml file I want in latter time (after the project generation)to add new file deployment.yaml under the app folder

deployment.yaml

apiVersion: v1
kind: Deployment
metadata:
  name: <%= appName %>  //this is the appname from the project generation

spec:
  replicas: <%= replica %>
  selector:
    app: <%= srvName %>

The appName & srvName are coming from the main generator, (I saw that there is option to share data between sub generator https://yeoman.io/authoring/storage.html , not sure how to share this between generators ) and the replica should come from the new/sub generator

This is the project structure after the generation

myapp   /root
 -app   /folder
  - service.yaml      /single file at the project generation
  - deployment.yaml   / new file added to the project under app folder

Like user start another generator/sub and have a new question e.g. how much replicas do you want? and then generates the file.

How can I do it ?

update This is my project strucutre

myapp
 - node_modules
 - package.json //here I declare the main-generator command -> tdk
 - generators
 -- app
 ---index.ts
 --deployment
 ---index.ts
 ---package.json    //here I declare the sub-generator command -> deploy
 - node_modules
 - package.json
 -.yo-rc.json       //here I see the data that I keep via config.set api

Update

When I call to the sub generator via program like

const yeoman = require('yeoman-environment'); const env = yeoman.createEnv();

env.lookup(function () {
    env.run("tdk:deploy", {
        replicas: 100
    }, (err) => {
        console.log("done", err);
    });
});

I got error:

out from config undefined : undefined //the undefind is from the console in the sub-generator

done TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined
    at validateString (internal/validators.js:125:11)
    at Object.join (path.js:1037:7)

I put a console.log in the subgenerator code like

  initializing() {
    this.srvName = this.config.get("srvName");
    this.appName = this.config.get("appName");
    console.log("out from config", this.srvName, ":", this.appName);
  }

And when I run the subgenerator I got empty config ( from the .yo-rc.json) while checking the .yo-rc.json . I was able to see the entry from the main generator, the data was stored but when I run it from the program it doesnt find it...any idea ?

This is the link for both project (very basic yeoman generator which demonstrate the point) just need to run npm install for both projects and for the generator run also npm link.

At the end: a project should be generated with two files

1. service.yaml   // generated from the main generator
2. deployment.yaml - // generated from sub generator with the properties from the main & sub generator

currently, the deployment.yaml file is not generated

https://drive.google.com/drive/folders/1kBnZxpVcRR9qhGZagVtod7W4wFmt73C6

1 . generator-tdk -  Generator and sub-generator 
2.  yeomanEnv - The code which is running the sub-generator to create the file inside the generated project

What am I doing wrong ? :(

if there is a way from the sub-generator to read the .yo-rc.json , it can help

like image 326
Jenny M Avatar asked Jun 12 '20 15:06

Jenny M


People also ask

What is yo RC JSON?

The . yo-rc. json file is a JSON file where configuration objects from multiple generators are stored. Each generator configuration is namespaced to ensure no naming conflicts occur between generators. This also means each generator configuration is sandboxed and can only be shared between sub-generators.

What is Yeoman NPM?

The Yeoman workflow comprises three types of tools for improving your productivity and satisfaction when building a web app: the scaffolding tool (yo), the build tool (Gulp, Grunt etc) and the package manager (like npm and Bower).

How do I create a generator in Yeoman?

A generator is, at its core, a Node.js module. First, create a folder within which you’ll write your generator. This folder must be named generator-name (where name is the name of your generator). This is important, as Yeoman relies on the file system to find available generators.

How does yeoman find the root of a project?

Most importantly, Yeoman searches the directory tree for a .yo-rc.json file. If found, it considers the location of the file as the root of the project. Behind the scenes, Yeoman will change the current directory to the .yo-rc.json file location and run the requested generator there.

How does yeoman work?

The generator system allows you to apply custom filters on every file writes. Automatically beautifying files, normalizing whitespace, etc, is totally possible. Once per Yeoman process, we will write every modified file to disk. This process is passed through a vinyl object stream (just like gulp ).

How do I create a generator from a yo project?

Run the yo generator command at the folder where you want to generate the generator project. You will be prompted to input the generator name and other details. The directory tree of the generator project will look like this: The generators\app\index.js file is the main entry of the generator.


1 Answers

You can set the values to config inside configuring of the main generator like this:

configuring() {
  this.config.set('appName', this.props.appName);
  this.config.set('srvName', this.props.srvName);
}

and read the values inside the sub-generators:

initializing() {
  this.srvName = this.config.get("srvName");
  this.appName = this.config.get("appName");
}

So you'll have access to these values via this.srvName and this.appName upon writing.

Example code:

app/index.js:

const Generator = require("yeoman-generator");

module.exports = class extends Generator {
  prompting() {
    this.props = {
      appName: "my-app",
      srvName: "my-service",
    };

    const prompts = [
      {
        name: "appName",
        message: "Project name: ",
        type: "input",
        default: this.props.appName,
      },
      {
        name: "srvName",
        message: "Service name: ",
        type: "input",
        default: this.props.srvName,
      },
    ];

    return this.prompt(prompts).then((props) => {
      this.props = props;
    });
  }

  configuring() {
    this.config.set('appName', this.props.appName);
    this.config.set('srvName', this.props.srvName);
  }

  writing() {
    this.fs.copyTpl(
      this.templatePath("app"),
      this.destinationPath(this.props.appName),
      this.props
    );
  }
};

deploy/index.js:

const Generator = require("yeoman-generator");

module.exports = class extends Generator {
  initializing() {
    this.srvName = this.config.get("srvName");
    this.appName = this.config.get("appName");
  }

  prompting() {
    this.props = {
      replicas: 0,
    };

    const prompts = [
      {
        name: "replica",
        message: "how much replicas do you want?",
        type: "input",
        default: this.props.replicas,
      },
    ];

    return this.prompt(prompts).then((props) => {
      this.props = props;
    });
  }

  writing() {
    this.fs.copyTpl(
      this.templatePath("deploy"),
      this.destinationPath(this.appName),
      {
        srvName: this.srvName,
        appName: this.appName,
        ...this.props,
      }
    );
  }
};

and commands:

yo <name for the main project generation

yo <name>:deploy to ask for replicas and create deployment.yaml


To execute the sub-generator without the use of yo:

var yeoman = require("yeoman-environment");
var env = yeoman.createEnv();

env.lookup(function () {
  env.run("<name>:deploy", {
      replicas: 100
  }, (err) => {
    console.log("done", err);
  });
});

and a sample sub-generator that skips question if values are passed via options (deploy/index.js):

const Generator = require("yeoman-generator");

module.exports = class extends Generator {
  initializing() {
    this.srvName = this.config.get("srvName");
    this.appName = this.config.get("appName");
  }

  prompting() {
    this.props = {
      replicas: 0,
    };

    const prompts = [
      {
        name: "replicas",
        message: "which app to generate?",
        type: "input",
        default: this.props.replicas,
        when: !this.options.replicas, // disable the question if it's found in options
      },
    ];

    return this.prompt(prompts).then((props) => {
      this.props = props;

      // set values from options (if found)
      this.props.replicas = this.options.replicas || this.props.replicas;
    });
  }


  writing() {
    this.fs.copyTpl(
      this.templatePath("deploy"),
      this.destinationPath(this.appName),
      {
        srvName: this.srvName,
        appName: this.appName,
        ...this.props,
      }
    );
  }
};
like image 96
Tibebes. M Avatar answered Oct 06 '22 00:10

Tibebes. M