balbuf / wasp
Write simple YAML to produce working WordPress code!
Requires
- php: >=5.4
- nikic/php-parser: ^3.1
- symfony/console: ^3.3
- symfony/event-dispatcher: ^3.3
- symfony/yaml: ^3.3
- twig/twig: ^1.35
This package is auto-updated.
Last update: 2024-11-29 05:39:18 UTC
README
Wasp is a tool for turning simple YAML configuration files into working WordPress code. Using a variety of "handlers", wasp parses your configuration file and produces the necessary code to register post types, taxonomies, menus, and much more! Wasp reduces the tedious nature of this work by leveraging sensible defaults and accepting user-defined defaults for complete flexibility.
Wasp is best employed as part of your build process, which means only the YAML file(s) would be stored in
your version control system. In the course of a build process, wasp parses the configuration file and
generates a PHP file that can act as the functions.php
file of a theme, the main plugin file of a plugin,
the entirety of an mu-plugin, or a file that is included by any of the former. However, wasp can also be used
to generate code that you can use as a starting point for your project, allowing you to save and modify it from
there. As such, if at any point you want to ditch wasp, you'd simply need to generate your code, save it, and
remove wasp from your project. Wasp gives you the benefit of a theme or plugin framework without ever having
to commit!
Installing
The preferred way to install wasp is via Composer:
$ composer require balbuf/wasp --dev
Wasp will now be available in composer's "bin dir", which is located in vendor/bin
by default.
You should be able to run wasp via:
$ vendor/bin/wasp
Wasp doesn't come with any handlers out of the box, so that you can leverage only the features you want and nothing more. In order to get started, you'll need to install one or more wasp plugins that contain handlers. All of the essential handlers for basic WordPress configuration can be found in balbuf/wasp-core:
$ composer require balbuf/wasp-core --dev
Creating a Config YAML File
The config YAML file can be named anything you'd like and stored anywhere, too.
When you run the generate
command, you will specify the location of your config file.
The only stipulation is that the file contain valid YAML and data that is properly formatted for each of your handlers.
The properties you set inside of your YAML file will depend upon the handlers you intend to use. Typically, one top-level property will be used by a single handler, but sometimes multiple handlers will act upon the same property for different purposes, or a handler may reference properties outside of the main one it handles to obtain additional information.
All config files should contain an wasp
property, which defines details about the file itself, e.g.:
wasp: # a unique prefix used to differentiate this project from others prefix: wasp_example # location of this file relative to the file root of the project dir: inc # how wasp should determine the public URL that corresponds to the file root of the project url_context: plugin
Additional properties will depend on the handlers you wish to use, and you should consult the documentation for the handlers' respective wasp plugin(s).
Generating Code
Generating code requires an input file path (to your YAML config file) and an output file path (to the generated PHP code), e.g.:
$ vendor/bin/wasp generate config.yml themes/my-theme/functions.php
The input file path and/or output file path can be replaced by -
to read data from STDIN
or write data
to STDOUT
, respectively.
Some handlers that work with additional files in your project (e.g. handlers that help with including files,
enqueing front-end assets, etc.) will need to know the path to your project's file root. The file root,
for example, would be the directory that contains your functions.php
file for a theme or the main
plugin file for a plugin. Knowing the location of the file root on the filesystem and the relative location of your
generated file within (specified via the wasp.dir
property of your config file) allows handlers to analyze files
on the filesystem and properly reference them from the generated PHP file.
In most cases, the file root can be discerned based on the output file path provided via the command line, in
combination with the wasp.dir
property specified in the config file. However, in the case of writing to
STDOUT
, explicitly specifying the project's file root may be required. For instance, the example above
could be rewritten as:
$ vendor/bin/wasp generate config.yml - --root=themes/my-theme > themes/my-theme/functions.php
If not specified or discernable from the output path, the file root defaults to the current working directory.
Adding Handlers
Handlers are typically added to your project by installing additional wasp plugins via composer. When wasp runs,
it analyzes your project's composer.lock
file to identify and execute all wasp plugins.
Considering all plugins/handlers installed in your project are activated by default, at times you may wish to disable certain plugins and/or handlers when generating your code.
- To disable a plugin, use the
--disable-plugin=
option along with the composer package name for the plugin - To disable a handler, use the
--skip-handler=
option along with the handler's machine name
Either option may be repeated to disable multiple plugins and/or handlers.
Wasp Config Properties
The wasp
top-level property contains information about the project and controls how code is generated.
Advanced Config
Wasp uses Twig to process its config values, allowing the user to reference
other properties within the config and perform some basic value processing via Twig's filters, functions, etc.
In order to use Twig in your config property values, you must enclose some or all of the value in Twig template
delimeters: {{ }}
for expressions and {% %}
for more complex control structures (loops, logic, etc.).
(Note that when a property value starts with a Twig template, the value must be quoted, otherwise the YAML
parser will get confused and interpret the value as an inline object, e.g. property: '{{ template }}'
instead
of the invalid property: {{ template }}
.)
Special Variables
Within a config property template, there are a number of special variables which allow access to surrounding property values, the current property chain, etc.
Because Twig is meant to be a templating engine which ultimately produces strings, special handling is necessary to
produce a non-string value for a property. An additional variable called output
is provided which has a setValue()
method to override the resolved value of the property template, e.g. {% do output.setValue(["this", "produces", "an", "array"]) %}
.
Similarly, the special this
, top
, and vars
variables return strings for other property values by default.
To access a property value of another type, the property must be appended with .getValue()
, e.g. this.sibling_prop.getValue()
;
as a shortcut, the property name can be appended with ()
to access the raw value, e.g. this.sibling_prop()
.
User Defaults
For many handlers whose property structure is an associative array of associative arrays, wasp provides a convenient way to provide default values to fill in those second-level associative arrays. To illustrate this type of property structure:
handler_property: thing1: name: Thing 1 color: red thing2: name: Thing 2
Notice how the handler's property is an associative array where the key (e.g. thing1
) represents a slug
(machine friendly identifier) for the item and the value of the item is another associative array containing
the item's properties and values (e.g. name
, color
, etc.). When the item arrays have many possible properties,
it's not uncommon to find yourself repeating the same value again and again for each item.
In order to reduce repetition, you can add a default
item whose properties are filled in for all the other items
when not explicitly set, e.g.:
handler_property: default: color: blue name: Untitled thing1: name: Thing 1 color: red thing2: name: Thing 2
Since thing2
did not specify a color
property, it inherits the color
value from the default
item (blue).
Self-referential Property Templates
Another feature of the default item is the ability to reference the same property in the main items when the items'
property values are resolved. Instead of just providing default values, the default item can also alter the
values of the properties in each main item. To make a self-referential template, the this
keyword can be used.
In the following example, we use the default post_type
property to add a prefix to the post type slug:
post_types: default: post_type: wasp_{{ this }} event: post_type: event testimonial: post_type: testimonial
When this config is resolved, the post_type
property for our items will be wasp_event
and wasp_testimonial
,
respectively.
Recipes
The advanced config using Twig can be a little complicated, so here are some example "recipes" to help illustrate the potential and allow you to get the most out of your config file with the least amount of work.
Conditional Prefix
The prefixed-post-type example above is great, but there are instances where you might need to use the post_type
wasp handler to modify a built-in post type or one provided by a plugin. In these instances, adding a prefix
to the post type value would be incorrect. We can make use of the env
object to turn off the prefix for
certain items, e.g.:
post_types: default: post_type: '{% if not env.noPrefix %}wasp_{{ this }}{% endif %}' event: post_type: event testimonial: post_type: testimonial post: post_type: post{% do env.set('noPrefix', true) %}
Now the resolved post_type
properties will be wasp_event
, wasp_testimonial
, and post
.
This could also be achieved by adding a custom property to the item:
post_types: default: post_type: '{% if not this.no_prefix %}wasp_{{ this }}{% endif %}' event: post_type: event testimonial: post_type: testimonial post: post_type: post no_prefix: true
This can make the config look a little cleaner, but the possible disadvantage is that the no_prefix
value
will be passed on to any handlers. In most cases the handler will ignore any properties it isn't expecting,
but the custom property could cause unintended consequences with some handlers.
Suffix with Default Value
Suppose you want to leverage a self-referential template while still providing a default value in case
the property was not set in one of the main items. This is possible, too! Take this imaginary images
handler, where we want to have a .jpeg
file extension automatically added to the file
property value,
while still providing a fallback file value:
images: default: file: '{{ ( this ?: "placeholder" ) ~ ".jpeg" }}' pic1: file: kittens alt: Kittens playing! pic2: file: puppies alt: Puppies playing! pic3: alt: Image coming soon!
When the config is resolved, the values of the file property will be kittens.jpeg
, puppies.jpeg
, and placeholder.jpeg
.
Default Array Values
When specifying script dependencies, it's not uncommon for many or all to require jquery
. We can set
this script (and any others) as the default base dependencies with the use of a default
object and a
custom property. The scripts
handler will look for the dependencies
property, so we will leverage
that property in the default
item and a custom property (additional_dependencies
) to merge our base
script dependencies with any additional dependencies for each item:
scripts: default: dependencies: '{% set deps = ["jquery"] %}{% do output.setValue(this ?: (this.additional_dependencies() is iterable ? this.additional_dependencies() | merge(deps) : deps)) %}' main: additional_dependencies: - jquery-ui navigation: dependencies: - underscore
For the main
item, it declares additional dependencies of jquery-ui
, causing it to use the default base
dependencies and producing ['jquery', 'jquery-ui']
. For the navigation
item, we don't need jquery
, so
we use the dependencies
property directly; the logic in our default dependencies
property respects this
and produces ['underscore']
for this item.
Extending
TODO: creating plugins, creating handlers, creating compilables, using compilables, including files, etc.