Cookiecutter vs Yeoman
Over the past few days, I've been trying to build a reusable boilerplate for Python packages (in the style that I write them). In the Python community, the natural choice is Cookiecutter, but I've been left disappointed by it.
I already knew of Yeoman from the JavaScript community, but hadn't written a generator for it yet. If I were to just search Google for "boilerplate generator" or "software scaffolding tool", Yeoman is the top relevant result. Granted, it seems odd to offer a Python project template through the Node ecosystem, but after a little experience with both, Yeoman seems more principled and more polished than Cookiecutter:
-
Yeoman has configuration functions out of the box. Cookiecutter puts the default configuration in a JSON file. Thus, Cookiecutter cannot handle dynamic defaults, e.g. pulling the default author from your Git configuration.
-
Yeoman has the concept of composable generators, which lets me reuse someone else's implementation of license choices. Cookiecutter does not have composable generators.
-
A Yeoman generator's dependencies are installed by your package manager when you install the generator. Cookiecutter requires the user to discover and install a generator's dependencies before running the generator.
-
Yeoman has better looking documentation, which in my opinion is a good indicator of project maturity. Cookiecutter has scrambled documentation. Some sections are incomplete, and some appear to be duplicates (e.g. "Learn the Basics of Cookiecutter by Creating a Cookiecutter" and "Create a Cookiecutter From Scratch").
-
At the time of this writing, Cookiecutter's master branch was last changed 4 months ago, and the docs were last changed 11 months ago, which suggests to me that the project is unmaintained. Given the gaps above, I do not believe this project is complete enough to transition to a maintenance mode.
That said, Yeoman has its own gaps.
-
It is not possible to pull data out of a subgenerator. One example is a license generator that returns the name of the chosen license, which another generator can insert into a package metadata file, e.g.
pyproject.toml
. -
The API uses promises in some places (e.g.
prompt
, via Inquirer) and callbacks in others (e.g.spawnCommand
), leaving me to promisify them to unlock convenient async/await syntax. -
The API documentation leaves much to be desired. The prompt API is documented in the tutorial, but not in the reference.
spawnCommand
is documented with return typeString
, when it really returns a Nodechild_process.ChildProcess
. -
The
Generator
base class has undocumented fields that you must not touch. I learned this the hard way by trying to use the propertyconfig
. -
Command-line options must be declared in the constructor. That means you cannot use asynchronous functions, like the one that looks up the GitHub username, to fill in default values.
-
Yeoman misses the opportunity to offer a command-line option for every prompt and skip the prompt if it were passed on the command-line. Even if I wanted to implement this behavior myself, it is tedious. The only way to detect the difference between an omitted command-line option and an explicit option that just happens to use the default value is to not declare a default, but that leaves me to manually document the default in the option description. Regardless, I still cannot document defaults that are computed from asynchronous functions.