- validate plugin against the template</code></pre><p>The plugin validation step uses the <ahref="#PkgTemplates.validate"><code>validate</code></a> function. It lets us catch mistakes before we try to generate packages.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.validate"href="#PkgTemplates.validate"><code>PkgTemplates.validate</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">validate(::Plugin, ::Template)</code></pre><p>Perform any required validation for a <ahref="#PkgTemplates.Plugin"><code>Plugin</code></a>.</p><p>It is preferred to do validation here instead of in <ahref="#PkgTemplates.prehook"><code>prehook</code></a>, because this function is called at <ahref="../user/#PkgTemplates.Template"><code>Template</code></a> construction time, whereas the prehook is only run at package generation time.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL240-L248">source</a></section></article><p>The package generation process looks like this:</p><pre><codeclass="language-none">- create empty directory for the package
- run plugin posthook</code></pre><p>As you can tell, plugins play a central role in setting up a package.</p><p>The three main entrypoints for plugins to do work are the <ahref="#PkgTemplates.prehook"><code>prehook</code></a>, the <ahref="#PkgTemplates.hook"><code>hook</code></a>, and the <ahref="#PkgTemplates.posthook"><code>posthook</code></a>. As the names might imply, they basically mean "before the main stage", "the main stage", and "after the main stage", respectively.</p><p>Each stage is basically identical, since the functions take the exact same arguments. However, the multiple stages allow us to depend on artifacts of the previous stages. For example, the <ahref="../user/#PkgTemplates.Git"><code>Git</code></a> plugin uses <ahref="#PkgTemplates.posthook"><code>posthook</code></a> to commit all generated files, but it wouldn't make sense to do that before the files are generated.</p><p>But what about dependencies within the same stage? In this case, we have <ahref="#PkgTemplates.priority"><code>priority</code></a> to define which plugins go when. The <ahref="../user/#PkgTemplates.Git"><code>Git</code></a> plugin also uses this function to lower its posthook's priority, so that even if other plugins generate files in their posthooks, they still get committed (provided that those plugins didn't set an even lower priority).</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.prehook"href="#PkgTemplates.prehook"><code>PkgTemplates.prehook</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">prehook(::Plugin, ::Template, pkg_dir::AbstractString)</code></pre><p>Stage 1 of the package generation process (the "before" stage, in general). At this point, <code>pkg_dir</code> is an empty directory that will eventually contain the package, and neither the <ahref="#PkgTemplates.hook"><code>hook</code></a>s nor the <ahref="#PkgTemplates.posthook"><code>posthook</code></a>s have run.</p><divclass="admonition is-info"><headerclass="admonition-header">Note</header><divclass="admonition-body"><p><code>pkg_dir</code> only stays empty until the first plugin chooses to create a file. See also: <ahref="#PkgTemplates.priority"><code>priority</code></a>.</p></div></div></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL251-L261">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.hook"href="#PkgTemplates.hook"><code>PkgTemplates.hook</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">hook(::Plugin, ::Template, pkg_dir::AbstractString)</code></pre><p>Stage 2 of the package generation pipeline (the "main" stage, in general). At this point, the <ahref="#PkgTemplates.prehook"><code>prehook</code></a>s have run, but not the <ahref="#PkgTemplates.posthook"><code>posthook</code></a>s.</p><p><code>pkg_dir</code> is the directory in which the package is being generated (so <code>basename(pkg_dir)</code> is the package name).</p><divclass="admonition is-info"><headerclass="admonition-header">Note</header><divclass="admonition-body"><p>You usually shouldn't implement this function for <ahref="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s. If you do, it should probably <code>invoke</code> the generic method (otherwise, there's not much reason to subtype <code>FilePlugin</code>).</p></div></div></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL264-L277">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.posthook"href="#PkgTemplates.posthook"><code>PkgTemplates.posthook</code></a> — <spanclass="docstring-category">Function</span></header><section><
end</code></pre><p>The <code>@plugin</code> macro defines some helpful methods for us. Inside of our struct definition, we're using <ahref="#PkgTemplates.default_file"><code>default_file</code></a> to refer to files in this repository.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.@plugin"href="#PkgTemplates.@plugin"><code>PkgTemplates.@plugin</code></a> — <spanclass="docstring-category">Macro</span></header><section><div><pre><codeclass="language-julia">@plugin struct ... end</code></pre><p>Define a plugin subtype with keyword constructors and default values.</p><p>For details on the general syntax, see <ahref="https://mauro3.github.io/Parameters.jl/stable/manual/#Types-with-default-values-and-keyword-constructors-1">Parameters.jl</a>.</p><p>There are a few extra restrictions:</p><ul><li>Before using this macro, you must have imported <code>@with_kw_noshow</code> via <code>using PkgTemplates: @with_kw_noshow</code></li><li>The type must be a subtype of <ahref="#PkgTemplates.Plugin"><code>Plugin</code></a> (or one of its abstract subtypes)</li><li>The type cannot be parametric</li><li>All fields must have default values</li></ul><p><strong>Example</strong></p><pre><codeclass="language-julia">using PkgTemplates: @plugin, @with_kw_noshow, Plugin
@plugin struct MyPlugin <: Plugin
x::String = "hello!"
y::Union{Int, Nothing} = nothing
end</code></pre><p><strong>Implementing <code>@plugin</code> Manually</strong></p><p>If for whatever reason, you are unable to meet the criteria outlined above, you can manually implement the methods that <code>@plugin</code> would have created for you. This is only mandatory if you want to use your plugin in interactive mode.</p><p><strong>Keyword Constructors</strong></p><p>If possible, use <code>@with_kw_noshow</code> to create a keyword constructor for your type. Your type must be capable of being instantiated with no arguments.</p><p><strong>Default Values</strong></p><p>If your type's fields have sensible default values, implement <code>defaultkw</code> like so:</p><pre><codeclass="language-julia">using PkgTemplates: PkgTemplates, Plugin
PkgTemplates.defaultkw(::Type{MyPlugin}, ::Val{:x}) = "my default"</code></pre><p>Remember to add a method to the function belonging to PkgTemplates, rather than creating your own function that PkgTemplates won't see.</p><p>If your plugin's fields have no sane defaults, then you'll need to implement <ahref="#PkgTemplates.prompt"><code>prompt</code></a> appropriately instead.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL4-L58">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.default_file"href="#PkgTemplates.default_file"><code>PkgTemplates.default_file</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">default_file(paths::AbstractString...) -> String</code></pre><p>Return a path relative to the default template file directory (<code>~/build/invenia/PkgTemplates.jl/templates</code>).</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL104-L109">source</a></section></article><p>The first method we implement for <code>Documenter</code> is <ahref="#PkgTemplates.gitignore"><code>gitignore</code></a>, so that packages created with this plugin ignore documentation build artifacts.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.gitignore"href="#PkgTemplates.gitignore"><code>PkgTemplates.gitignore</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">gitignore(::Plugin) -> Vector{String}</code></pre><p>Return patterns that should be added to <code>.gitignore</code>. These are used by the <ahref="../user/#PkgTemplates.Git"><code>Git</code></a> plugin.</p><p>By default, an empty list is returned.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL175-L182">source</a></section></article><p>Second, we implement <ahref="#PkgTemplates.badges"><code>badges</code></a> to add a couple of badges to new packages' README files.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.badges"href="#PkgTemplates.badges"><code>PkgTemplates.badges</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">badges(::Plugin) -> Union{Badge, Vector{Badge}}</code></pre><p>Return a list of <ahref="#PkgTemplates.Badge"><code>Badge</code></a>s, or just one, to be added to <code>README.md</code>. These are used by the <ahref="../user/#PkgTemplates.Readme"><code>Readme</code></a> plugin to add badges to the README.</p><p>By default, an empty list is returned.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL185-L192">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.Badge"href="#PkgTemplates.Badge"><code>PkgTemplates.Badge</code></a> — <spanclass="docstring-category">Type</span></header><section><div><pre><codeclass="language-julia">Badge(hover::AbstractString, image::AbstractString, link::AbstractString)</code></pre><p>Container for Markdown badge data. Each argument can contain placeholders, which will be filled in with values from <ahref="#PkgTemplates.combined_view"><code>combined_view</code></a>.</p><p><strong>Arguments</strong></p><ul><li><code>hover::AbstractString</code>: Text to appear when the mouse is hovered over the badge.</li><li><code>image::AbstractString</code>: URL to the image to display.</li><li><code>link::AbstractString</code>: URL to go to upon clicking the badge.</li></ul></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTempl
end</code></pre><p>We didn't use <code>@plugin</code> for this one, because there are no fields. Validation and all three hooks are implemented:</p><ul><li><ahref="#PkgTemplates.validate"><code>validate</code></a> makes sure that all required Git configuration is present.</li><li><ahref="#PkgTemplates.prehook"><code>prehook</code></a> creates the Git repository for the package.</li><li><ahref="#PkgTemplates.hook"><code>hook</code></a> generates the <code>.gitignore</code> file, using the special <ahref="#PkgTemplates.gitignore"><code>gitignore</code></a> function.</li><li><ahref="#PkgTemplates.posthook"><code>posthook</code></a> adds and commits all the generated files.</li></ul><p>As previously mentioned, we use <ahref="#PkgTemplates.priority"><code>priority</code></a> to make sure that we wait until all other plugins are finished their work before committing files.</p><p>Hopefully, this demonstrates the level of control you have over the package generation process when developing plugins, and when it makes sense to exercise that power!</p><h2id="FilePlugin-Walkthrough-1"><aclass="docs-heading-anchor"href="#FilePlugin-Walkthrough-1"><code>FilePlugin</code> Walkthrough</a><aclass="docs-heading-anchor-permalink"href="#FilePlugin-Walkthrough-1"title="Permalink"></a></h2><p>Most of the time, you don't really need all of the control that we showed off above. Plugins that subtype <ahref="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a> perform a much more limited task. In general, they just generate one templated file.</p><p>To illustrate, let's look at the <ahref="../user/#PkgTemplates.Citation"><code>Citation</code></a> plugin, which creates a <code>CITATION.bib</code> file.</p><pre><codeclass="language-julia">@plugin struct Citation <: FilePlugin
)</code></pre><p>Similar to the <code>Documenter</code> example above, we're defining a keyword constructor, and assigning a default template file from this repository. This plugin adds nothing to <code>.gitignore</code>, and it doesn't add any badges, so implementations for <ahref="#PkgTemplates.gitignore"><code>gitignore</code></a> and <ahref="#PkgTemplates.badges"><code>badges</code></a> are omitted.</p><p>First, we implement <ahref="#PkgTemplates.source"><code>source</code></a> and <ahref="#PkgTemplates.destination"><code>destination</code></a> to define where the template file comes from, and where it goes. These functions are specific to <ahref="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, and have no effect on regular <ahref="#PkgTemplates.Plugin"><code>Plugin</code></a>s by default.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.source"href="#PkgTemplates.source"><code>PkgTemplates.source</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">source(::FilePlugin) -> Union{String, Nothing}</code></pre><p>Return the path to a plugin's template file, or <code>nothing</code> to indicate no file.</p><p>By default, <code>nothing</code> is returned.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL195-L201">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.destination"href="#PkgTemplates.destination"><code>PkgTemplates.destination</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">destination(::FilePlugin) -> String</code></pre><p>Return the destination, relative to the package root, of a plugin's configuration file.</p><p>This function <strong>must</strong> be implemented.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/plugin.jl#LL204-L210">source</a></section></article><p>Next, we implement <ahref="#PkgTemplates.tags"><code>tags</code></a>. We briefly saw this function earlier, but in this case it's necessary to change its behaviour from the default. To see why, it might help to see the template file in its entirety:</p><pre><codeclass="language-none">@misc{<<&PKG>>.jl,
}</code></pre><p>Because the file contains its own <code>{}</code> delimiters, we need to use different ones for templating to work properly.</p><p>Finally, we implement <ahref="#PkgTemplates.view"><code>view</code></a> to fill in the placeholders that we saw in the template file.</p><h2id="Doing-Extra-Work-With-FilePlugins-1"><aclass="docs-heading-anchor"href="#Doing-Extra-Work-With-FilePlugins-1">Doing Extra Work With <code>FilePlugin</code>s</a><aclass="docs-heading-anchor-permalink"href="#Doing-Extra-Work-With-FilePlugins-1"title="Permalink"></a></h2><p>Notice that we didn't have to implement <ahref="#PkgTemplates.hook"><code>hook</code></a> for our plugin. It's implemented for all <ahref="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, like so:</p><pre><codeclass="language-julia">function render_plugin(p::FilePlugin, t::Template, pkg::AbstractString)
end</code></pre><p>But what if we want to do a little more than just generate one file?</p><p>A good example of this is the <ahref="../user/#PkgTemplates.Tests"><code>Tests</code></a> plugin. It creates <code>runtests.jl</code>, but it also modifies the <code>Project.toml</code> to include the <code>Test</code> dependency.</p><p>Of course, we could use a normal <ahref="#PkgTemplates.Plugin"><code>Plugin</code></a>, but it turns out there's a way to avoid that while still getting the extra capbilities that we want.</p><p>The plugin implements its own <code>hook</code>, but uses <code>invoke</code> to avoid duplicating the file creation code:</p><pre><codeclass="language-julia">@plugin struct Tests <: FilePlugin
end</code></pre><p>There is also a default <ahref="#PkgTemplates.validate"><code>validate</code></a> implementation for <ahref="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, which checks that the plugin's <ahref="#PkgTemplates.source"><code>source</code></a> file exists, and throws an <code>ArgumentError</code> otherwise. If you want to extend the validation but keep the file existence check, use the <code>invoke</code> method as described above.</p><p>For more examples, see the plugins in the <ahref="../user/#Continuous-Integration-(CI)-1">Continuous Integration (CI)</a> and <ahref="../user/#Code-Coverage-1">Code Coverage</a> sections.</p><h2id="Supporting-Interactive-Mode-1"><aclass="docs-heading-anchor"href="#Supporting-Interactive-Mode-1">Supporting Interactive Mode</a><aclass="docs-heading-anchor-permalink"href="#Supporting-Interactive-Mode-1"title="Permalink"></a></h2><p>When it comes to supporting interactive mode for your custom plugins, you have two options: write your own <ahref="#PkgTemplates.interactive"><code>interactive</code></a> method, or use the default one. If you choose the first option, then you are free to implement the method however you want. If you want to use the default implementation, then there are a few functions that you should be aware of, although in many cases you will not need to add any new methods.</p><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.interactive"href="#PkgTemplates.interactive"><code>PkgTemplates.interactive</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">interactive(T::Type{<:Plugin}) -> T</code></pre><p>Interactively create a plugin of type <code>T</code>. Implement this method and ignore other related functions only if you want completely custom behaviour.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/interactive.jl#LL13-L18">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.prompt"href="#PkgTemplates.prompt"><code>PkgTemplates.prompt</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">prompt(::Type{P}, ::Type{T}, ::Val{name::Symbol}) -> Any</code></pre><p>Prompts for an input of type <code>T</code> for field <code>name</code> of plugin type <code>P</code>. Implement this method to customize particular fields of particular types.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/interactive.jl#LL112-L117">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.customizable"href="#PkgTemplates.customizable"><code>PkgTemplates.customizable</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">customizable(::Type{<:Plugin}) -> Vector{Pair{Symbol, DataType}}</code></pre><p>Return a list of keyword arguments that the given plugin type accepts, which are not fields of the type, and should be customizable in interactive mode. For example, for a constructor <code>Foo(; x::Bool)</code>, provide <code>[x => Bool]</code>. If <code>T</code> has fields which should not be customizable, use <code>NotCustomizable</code> as the type.</p></div><aclass="docs-sourcelink"target="_blank"href="https://github.com/invenia/PkgTemplates.jl/blob/087dcc8d05ce31059cf860b75f846479d6685749/src/interactive.jl#LL48-L55">source</a></section></article><articleclass="docstring"><header><aclass="docstring-binding"id="PkgTemplates.input_tips"href="#PkgTemplates.input_tips"><code>PkgTemplates.input_tips</code></a> — <spanclass="docstring-category">Function</span></header><section><div><pre><codeclass="language-julia">input_tips(::Type{T}) -> Vector{String}</code></pre><p>Provide some extra tips