yeoman / environment

Yeoman runtime environment
BSD 2-Clause "Simplified" License
128 stars 71 forks source link

Embedding yeoman to run a generator programmatically #441

Closed eighty4 closed 2 years ago

eighty4 commented 2 years ago

I'm trying to use a generator with yeoman-environment, specifying in my dependencies like so:

{
  "dependencies": {
    "generator-myapp": "0.0.1",
    "yeoman-environment": "3.12.1"
  }
}

I first tried environment.lookup and environment.lookupGenerator but they exit cleanly and always return undefined.

import yeoman from 'yeoman-environment'
const environment = yeoman.createEnv()
environment.lookupGenerator('myapp', {localOnly: true})

I couldn't find any getters that I could use to inspect the state after doing the lookup call, but it completes without error or anything returned.

Then I found environment.register(name: string, namespace: string, packagePath: string) which looks promising, since my use case doesn't require loading any other generators than the one from my npm dependencies. When I call this, I get a MODULE_NOT_FOUND error with the path generator-myapp/ (with the slash appended to the package name):

Error: Cannot find module 'generator-trousers/'
Require stack:
 ...
 ...
      at Environment.resolveModulePath (yeoman-environment@3.12.1/node_modules/yeoman-environment/lib/environment.js:1203:32)
    at Environment.register (yeoman-environment@3.12.1/node_modules/yeoman-environment/lib/environment.js:468:29)

The code I'm using:

const env = yeoman.createEnv()
env.register('generator-myapp', 'myapp', './node_modules/generator-myapp')

(I get the same result if I only use the first parameter or all three)

Here is where the error is thrown: https://github.com/yeoman/environment/blob/bd32c649529dc128c818348601abfdb6f5c8ec09/lib/environment.js#L1203

and here is where the path separator is being appended: https://github.com/yeoman/environment/blob/bd32c649529dc128c818348601abfdb6f5c8ec09/lib/environment.js#L1192

I'm not able to figure out what the arguments should be for this method or if there's another approach to solving this problem I'm overlooking. It looks like the first argument name is being used as the modulePath whether or not you use the other arguments for register.

So then if I use env.register('./node_modules/generator-myapp') I get Error: Cannot find module '/Users/me/.../appUsingYeoman/node_modules/generator-trousers/'. Yikes!

I appreciate any guidance you can offer. Thanks!

eighty4 commented 2 years ago

Alright, so I got the generator to run programmatically using the full path to the default app generator.

With this generator:

const Generator = require('yeoman-generator')
module.exports = class extends Generator {
    constructor(args, opts) {
        super(args, opts)
    }
    method1() {
        console.log('woo')
    }
}

and this generator:

const env = yeoman.createEnv()
env.register('./node_modules/generator-myapp/app')
env.run('myapp')
    .then(() => {
        console.log('success!')
    }, (err) => {
        console.log('error', err)
    })

I get this output:

woo

No change to package.json was detected. No package manager install will be executed.
success!

I got that part working.

eighty4 commented 2 years ago

Is is possible to turn off the logger? There's an extra newline and a log message printed out.

I'd like to embed yeoman and only show prompts configured in the generator and any other events handled programmatically. I see the Adapter type but it looks like it logs to console.log and only stdout and stdin for use by the generator prompts and output are configurable.

mshima commented 2 years ago

I recommend implementing like https://github.com/yeoman/environment/blob/main/cli/index.js

eighty4 commented 2 years ago

Do you know how I could disable Yeoman's logging?

mshima commented 2 years ago

Providing a custom terminal adapter: https://github.com/yeoman/environment/blob/main/lib/adapter.js