<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>grunt Archives - Alexandros Georgiou</title>
	<atom:link href="https://www.alexgeorgiou.gr/tag/grunt/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.alexgeorgiou.gr/tag/grunt/</link>
	<description>Balancing brackets for a living</description>
	<lastBuildDate>Wed, 20 Dec 2023 10:31:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.alexgeorgiou.gr/wp-content/uploads/2021/07/cropped-alexgeorgiou-icon-32x32.png</url>
	<title>grunt Archives - Alexandros Georgiou</title>
	<link>https://www.alexgeorgiou.gr/tag/grunt/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>💩 Your WordPress PHP code stinks! Here&#8217;s why.</title>
		<link>https://www.alexgeorgiou.gr/wordpress-php-code-quality/</link>
					<comments>https://www.alexgeorgiou.gr/wordpress-php-code-quality/#comments</comments>
		
		<dc:creator><![CDATA[alexg]]></dc:creator>
		<pubDate>Mon, 22 Jul 2019 18:12:52 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[code quality]]></category>
		<category><![CDATA[code style]]></category>
		<category><![CDATA[envato]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[grunt-phpcs]]></category>
		<category><![CDATA[phan]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[phpcbf]]></category>
		<category><![CDATA[phpcs]]></category>
		<category><![CDATA[phpdoc]]></category>
		<category><![CDATA[phpunit]]></category>
		<category><![CDATA[requirements]]></category>
		<category><![CDATA[themeforest]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[wordpress.org]]></category>
		<guid isPermaLink="false">https://www.alexgeorgiou.gr/?p=282</guid>

					<description><![CDATA[<p>Developing a WordPress theme or plugin? Learn how to use phan and phpcs to improve the quality of your WordPress PHP code.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/wordpress-php-code-quality/">💩 Your WordPress PHP code stinks! Here&#8217;s why.</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In software development, as in all forms of engineering, we strive to produce results that satisfy a multitude of constraints, some more obvious than others. High <em>code quality</em> should be in your list of constraints when you write WordPress PHP code. And there are tools out there that can help you!</p>



<h3 class="wp-block-heading">First, some theory</h3>



<p>In a fantasy world where unicorns poop chocolate fudge and everything is awesome, it all goes down somewhat like this: You talk with the client or end user, and you gather up a list of things they want. You collect and number these in a <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://en.wikipedia.org/wiki/User_requirements_document" target="_blank">requirements document</a>, braking them up into <strong>Functional</strong> requirements, <strong>Non-functional</strong> requirements, and other <strong>Design constraints</strong>. Then, you design a solution, write your tests, and implement your code. Once all of the requirements in the document are met, you are <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://www.agilealliance.org/glossary/definition-of-done/" target="_blank">done</a>, and you get paid. Easy! Deviate from this simple methodology <strong>at your own peril.</strong> (Oh, and you also then need to maintain, fix and improve the code you&#8217;ve written.)</p>



<p>As a software engineer, you should know that in addition to what the client asks for, there are always <em>implicit</em> requirements. For example, your code must be <strong>readable</strong> and <strong>maintainable</strong>, and it must be <strong>reliable</strong> (read: not too buggy). All of these are aspects of <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://en.wikipedia.org/wiki/Software_quality" target="_blank">code quality</a>. A client or end user will never ask you for these, but you should have them in your list of non-functional requirements.</p>



<p>Professional developers write simple, clear code where a novice would write complex, intricate code to solve that same problem. Sure, a lot of this is just a matter of practice and experience.</p>



<p>Dev tools can help you with code quality whether you&#8217;re novice or pro. They help you discover issues that you don&#8217;t know about, and they help you discover issues that you do know about, faster!</p>



<p>If you are developing themes or plugins, here&#8217;s two tools that you definitely want to use as a professional WordPress developer:</p>



<h3 class="wp-block-heading">Improve WordPress PHP <a href="https://en.wikipedia.org/wiki/Code_smell" target="_blank" rel="noreferrer noopener" aria-label="code smell (opens in a new tab)">code smell</a> with CodeSniffer</h3>



<p>There are some <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://codex.wordpress.org/WordPress_Coding_Standards" target="_blank">styling guidelines</a> that all WordPress developers should follow. Especially if you are aspiring to upload your code to wordpress.org, or to the Envato Market (ThemeForest or CodeCanyon), then you definitely want to follow these guidelines. Generally, these get encoded into your muscle memory pretty quickly, but it&#8217;s always a great idea to have a tool like <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://github.com/squizlabs/PHP_CodeSniffer" target="_blank">PHP CodeSniffer</a> that double-checks your code style.</p>



<p>On my Ubuntu machine, I was able to install PHP CodeSniffer easily with:</p>



<pre class="wp-block-preformatted">sudo apt install php-codesniffer</pre>



<p>Then, it&#8217;s just a matter of loading the <a href="https://github.com/WordPress/WordPress-Coding-Standards" target="_blank" rel="noreferrer noopener" aria-label=" (opens in a new tab)">WordPress-specific set of rules</a>. Go to your home directory and clone the project:</p>



<pre class="wp-block-preformatted">git clone https://github.com/WordPress/WordPress-Coding-Standards ~/wpcs</pre>



<p>And tell CodeSniffer the location of these rules:</p>



<pre class="wp-block-preformatted">sudo phpcs --config-set installed_paths ~/wpcs</pre>



<p>Check that <a href="https://github.com/WordPress/WordPress-Coding-Standards#standards-subsets" target="_blank" rel="noreferrer noopener" aria-label=" (opens in a new tab)">the new rules</a> are added into CodeSniffer with:</p>



<pre class="wp-block-preformatted">phpcs -i</pre>



<p>Congratulations! You can now check your plugin for code style with a command such as:</p>



<pre class="wp-block-preformatted">phpcs --standard=Wordpress-Core /path/to/source/code/root/dir</pre>



<p>If, <a href="https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/">like me</a>, you&#8217;re using <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://gruntjs.com/" target="_blank">grunt</a> for your build process, then there&#8217;s a nifty Grung plugin, <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://github.com/SaschaGalley/grunt-phpcs" target="_blank">grunt-phpcs</a>. Just make sure to specify <code>WordPress-core</code> as the rule set. Here&#8217;s a <code>grunt</code> target that you might use:</p>



<pre class="wp-block-preformatted">phpcs: {
    plugin: {
        src: ['src/**/*.php']
    },
    options: {
        bin: '/usr/bin/phpcs',
        standard: 'WordPress-core'
    }
}</pre>



<p>Run it against your code and you will get a number of improvement suggestions. Some of these can be applied automatically with <code>PHPCBF</code> (the <strong>PHP Code Beautifier and Fixer</strong>), or you can go through the list manually and apply each suggestion as you see fit. Many of the suggestions will be related to code indentation, but you will also see a large number of other suggestions that are more critical.</p>



<h3 class="wp-block-heading">Improve WordPress PHP code correctness with phan</h3>



<p>PHP is a very lenient language. It will let you get away with murder. This is something that novice programmers often enjoy. For professionals it&#8217;s a nightmare, as it makes spotting errors harder. PHP has thus gained somewhat of a notoriety for being a bad language, and is the butt of some <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://www.reddit.com/r/ProgrammerHumor/comments/6k40cb/working_at_pornhub/" target="_blank">clever jokes</a>.</p>



<p>This isn&#8217;t something to worry about. It has happened to many respectable languages, including JavaScript. So nowadays we have <code>use strict</code>, which lets us only use <a rel="noreferrer noopener" aria-label="The Good Parts (opens in a new tab)" href="http://shop.oreilly.com/product/9780596517748.do" target="_blank">The Good Parts</a> of the language. You can do something similar with PHP.</p>



<p>My point is that <a href="https://en.wikipedia.org/wiki/Static_program_analysis">static code analysis</a> is not the hero you want, but it&#8217;s definitely the hero you deserve. Enter <code><a rel="noreferrer noopener" aria-label="phan (opens in a new tab)" href="https://github.com/phan/phan" target="_blank">phan</a></code>:</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="PHP in 2018 by the Creator of PHP" width="840" height="473" src="https://www.youtube.com/embed/rKXFgWP-2xQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div><figcaption><a href="https://en.wikipedia.org/wiki/Rasmus_Lerdorf">Rasmus Lerdorf</a>, the creator of PHP, discusses static code analysis with phan</figcaption></figure>



<p>First, install it. It&#8217;s straightforward to install <code>phan</code> with <code>composer</code>:</p>



<pre class="wp-block-preformatted">composer require phan/phan</pre>



<p>Now <code>phan</code> lives in your project&#8217;s <code>vendor/</code> dir.</p>



<p>Next, create a configuration under your project dir, in <code>.phan/config.php</code>. This will tell <code>phan</code> what settings you want to run it with. Start with the <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file" target="_blank">example given here</a>, and set your source code directories.</p>



<p>You will also want to point to some third-party code, including the directory of your WordPress installation, since your code will invariably use WordPress functions and types.</p>



<p>You can also use this config file to exclude some rules, so that <code>phan</code> does not check for them. <a href="https://github.com/phan/phan/blob/master/.phan/plugins/README.md">Here&#8217;s the complete list of <code>phan</code> <em>plugins</em></a>.</p>



<p>When all is set, you can call <code>phan</code> on your code with:</p>



<pre class="wp-block-preformatted">vendor/bin/phan</pre>



<p>I don&#8217;t care how pro you are, you will definitely get a list of suggestions on how to improve your code.</p>



<h3 class="wp-block-heading">Conclusion</h3>



<p>Using <code>phpcs</code> and <code>phan</code> together, you will avoid a large number of errors that would otherwise likely go undetected. These include errors with translator comments over strings, <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://phpdoc.org/" target="_blank">phpDoc</a> formatting errors, variable type errors, errors related to sentinel values such as <code>null</code>, and array indexing errors.</p>



<p>Always use these two tools together with <code>phpunit</code> when you write code. You will write more readable, maintainable, correct and robust code that you can feel confident about.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/wordpress-php-code-quality/">💩 Your WordPress PHP code stinks! Here&#8217;s why.</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.alexgeorgiou.gr/wordpress-php-code-quality/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>😛 How to make pot with WordPress</title>
		<link>https://www.alexgeorgiou.gr/make-pot-wordpress/</link>
					<comments>https://www.alexgeorgiou.gr/make-pot-wordpress/#respond</comments>
		
		<dc:creator><![CDATA[alexg]]></dc:creator>
		<pubDate>Thu, 29 Mar 2018 18:08:46 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[grunt-po2mo]]></category>
		<category><![CDATA[grunt-pot]]></category>
		<category><![CDATA[grunt-wp-i18n]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[l10n]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[pot]]></category>
		<category><![CDATA[translation]]></category>
		<category><![CDATA[wordpress]]></category>
		<guid isPermaLink="false">https://www.alexgeorgiou.gr/?p=241</guid>

					<description><![CDATA[<p>Build translation files to localize a WordPress plugin using grunt. Create separate translation files for the front-end and back-end.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/make-pot-wordpress/">😛 How to make pot with WordPress</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>In this article I will detail how to use grunt to build translation files to <a href="https://developer.wordpress.org/plugins/internationalization/localization/" target="_blank" rel="noopener">localize a WordPress plugin</a>. We&#8217;ll create separate translation files for the front-end and back-end (admin area), potentially allowing a translator to translate only one or the other.</em></p>
<h2>Have you tried POT?</h2>
<p>Hopefully you&#8217;ve already used the i18n functions in your PHP code. For example, a simple translatable string would look like:</p>
<pre>$text = <a href="https://developer.wordpress.org/reference/functions/__/" target="_blank" rel="noopener">__</a>( 'Text to internationalize', 'language-domain-slug' );</pre>
<p>The purpose of this article is not to list all the relevant functions, you can look them up <a href="https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/" target="_blank" rel="noopener">here</a>. These are all wrappers to <a href="https://www.gnu.org/software/gettext/" target="_blank" rel="noopener">gettext</a>.</p>
<p>The important thing to note is that you need to run something to generate your <em>Portable Object Template</em> files. These <code>.pot</code> files can then be copied to <em>Portable Object</em> <code>.po</code> files, then translated to <em>Machine Object</em> <code>.mo</code> files. These <code>.mo</code> binaries are finally loaded by the end user&#8217;s WordPress installation to provide the translated strings to the plugin, or theme. No surprises up to here, all of this is standard.</p>
<h2>Grow your own with Grunt!</h2>
<p>What I will present today is an easy way to use <a href="https://gruntjs.com" target="_blank" rel="noopener">grunt</a> to generate the following:</p>
<ol>
<li>Two <code>.pot</code> files, one with the front-end strings and one with the admin area strings, and</li>
<li><code>.mo</code> files for any <code>.po</code> files in your project</li>
</ol>
<p>You will still need to copy the <code>.pot</code> files to <code>.po</code> files manually and provide the translations. Let&#8217;s get started:</p>
<p>The first thing to add to our <code>Gruntfile.js</code> is <a href="https://www.npmjs.com/package/grunt-wp-i18n" target="_blank" rel="noopener">grunt-wp-i18n</a>. This is a grunt plugin that I have found to be a little more flexible than the alternative, <a href="https://www.npmjs.com/package/grunt-pot" target="_blank" rel="noopener">grunt-pot</a>. So, as usual, add this grunt plugin to your dev toolbox:</p>
<div class="highlight shell">
<pre class="line"><span class="source shell">npm install grunt-wp-i18n --save-dev</span></pre>
<div class="line">and then add the following into your Gruntfile, to get the plugin loaded:</div>
<div class="line">
<div class="highlight js">
<pre class="line"><span class="source js"><span class="variable other object js">grunt</span><span class="meta js"><span class="meta delimiter method period js">.</span><span class="entity name function js">loadNpmTasks</span><span class="punctuation definition begin round js">(</span> <span class="string quoted single js"><span class="punctuation definition string begin js">'</span>grunt-wp-i18n<span class="punctuation definition string end js">'</span></span> <span class="punctuation definition end round js">)</span></span><span class="punctuation terminator statement js">;</span></span></pre>
<div class="line">Now we need to configure this build task. We will hide our pot in our usual stash, the <code>/languages</code> directory of our WordPress plugin. I like to keep my source code in version control under <code>/src</code> and copy it over to assemble the build under <code>/build</code>. I keep any <code>.po</code> translations under <code>/src/languages</code>, and auto-generate <code>.pot</code> files into the <code>/build/languages</code> dir. Any <code>.po</code> files are simply copied from the <code>/src</code> to the <code>/build</code> directory. This is a good practice that keeps our pot always fresh!</div>
<pre class="line">makepot: {
    front: {
        options: {
            cwd: 'build',
            potFilename: 'myplugin-front.pot',
            include: [ '/path/to/files', '/more/files', '/etc' ],
            domainPath: 'languages/',
            type: 'wp-plugin'
        }
    },
    admin: {
        options: {
            cwd: 'build',
            potFilename: 'myplugin.pot',
            include: [ '.*' ],
            exclude: [ '/path/to/files', '/more/files', '/etc' ],
            domainPath: 'languages/',
            type: 'wp-plugin'
    }
}</pre>
<div class="line">Notice that in this example there are two files: The <code>myplugin-front.pot</code> file pulls strings from a list of files that make up the front-end, while the <code>myplugin.pot</code> pulls strings from every other thing in my plugin, which would be the admin area.</div>
<blockquote>
<div class="line">Now a translator is free to only translate the front-end, leaving the back-end, which may contain a lot more strings, to its default (English?) language.</div>
</blockquote>
<div class="line">In functions such as <a href="https://developer.wordpress.org/reference/functions/__/" target="_blank" rel="noopener">__()</a>, the second argument is a <em>language domain</em>. People usually set here the plugin&#8217;s slug, but this is not strictly necessary: You can use different domains for e.g. the front-end and the back-end. Here we will use <code>myplugin-front</code> for the front-end and <code>myplugin</code> for the back-end. This has to be done in the PHP code, wherever the i18n functions are used.</div>
</div>
</div>
</div>
<h2 class="line">Make pot</h2>
<div class="line">If all is set up correctly, you can now easily make pot with:</div>
<pre class="line">grunt makepot</pre>
<p class="line">This will make two files in your project, <code>/build/languages/myplugin.pot</code> and <code>/build/languages/myplugin-frontend.pot</code>. Do not commit these files, we&#8217;ll let them auto-generate every time. The <code>/build</code> directory is in my <code>.gitignore</code> list.</p>
<h2 class="line">Using pot</h2>
<div class="line">For your plugin to actually use this, you need to copy the <code>.pot</code> files into <code>.po</code> files. Depending on your language, you will have to use the correct <em>language code</em> in your filename. See the <a href="https://www.gnu.org/software/gettext/manual/gettext.html#Language-Codes" target="_blank" rel="noopener">gettext list of language codes</a> or simply the <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" target="_blank" rel="noopener">Wikipedia list of ISO 639-1 language codes</a>. For instance, to do a German translation in our example, you would need to copy the following:</div>
<pre class="line">cp build/languages/myplugin-front.pot src/languages/myplugin-front-de_DE.po
cp build/languages/myplugin.pot src/languages/myplugin-de_DE.po</pre>
<div class="line">or to do a translation to Arabic:</div>
<div class="line">
<pre class="line">cp build/languages/myplugin-front.pot src/languages/myplugin-front-ar.po
cp build/languages/myplugin.pot src/languages/myplugin-ar.po</pre>
<div class="line">You will need to check to make sure whether you need to use a <em>country code</em> along with your language code.</div>
<div class="line">Once you have your <code>.po</code> files you can load them up in <a href="https://poedit.net/" target="_blank" rel="noopener">poedit</a> and start translating. Once the translations are ready you can commit them to your source tree.</div>
<h2 class="line">Packaging it all together for shipment</h2>
<div class="line">WordPress will not use your <code>.pot</code> or <code>.po</code> files. You only include these in your plugin so as to help future translators. To actually enable translation you need to convert the <code>.po</code> files to <code>.mo</code> files.</div>
</div>
<p>We&#8217;ll use another grunt plugin, <a href="https://www.npmjs.com/package/grunt-po2mo" target="_blank" rel="noopener">grunt-po2mo</a>. First add it to your project:</p>
<div class="highlight shell">
<pre class="line"><span class="source shell">npm install grunt-po2mo --save-dev</span></pre>
</div>
<p>And add the following into your Gruntfile to load it:</p>
<div class="highlight js">
<pre class="line"><span class="source js"><span class="variable other object js">grunt</span><span class="meta js"><span class="meta delimiter method period js">.</span><span class="entity name function js">loadNpmTasks</span><span class="punctuation definition begin round js">( </span><span class="string quoted single js"><span class="punctuation definition string begin js">'</span>grunt-po2mo<span class="punctuation definition string end js">' </span></span><span class="punctuation definition end round js">)</span></span><span class="punctuation terminator statement js">;</span></span></pre>
</div>
<p>This one requires very little configuration. Add the following target into your Gruntfile:</p>
<pre>po2mo: {
    plugin: {
        cwd: 'src',
        src: 'languages/*.po',
        dest: 'build',
        expand: true
    }
},</pre>
<p>This config expects that the <code>.po</code> files are in the <code>/src/languages</code> directory where we placed them earlier. It places the <code>.mo</code> files in the <code>/build/languages</code> dir. All you need to do for this is:</p>
<pre>grunt po2mo</pre>
<p>You will also need to tell your plugin to actually go and use these <code>.mo</code> files. Easy. Just hook to the <code>plugins_loaded</code> action and load the text domains:</p>
<pre>function action_plugins_loaded() {
    load_plugin_textdomain( 'myplugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
    load_plugin_textdomain( 'myplugin-front', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
} // end function action_plugins_loaded

add_action( 'plugins_loaded', 'action_plugins_loaded' );</pre>
<h2>That&#8217;s it!</h2>
<p>Make sure to include the <code>makepot</code> and <code>po2mo</code> targets in your build task. Finally use <code>grunt-contrib-compress</code> or another plugin to compress your entire build directory to a <code>.zip</code> file that is ready to be installed into WordPress!</p>
<p>Pass it on.</p>
<p>&nbsp;</p>
<p>The post <a href="https://www.alexgeorgiou.gr/make-pot-wordpress/">😛 How to make pot with WordPress</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.alexgeorgiou.gr/make-pot-wordpress/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>🐗 Building WordPress plugins with an increasingly verbose Gruntfile</title>
		<link>https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/</link>
					<comments>https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/#comments</comments>
		
		<dc:creator><![CDATA[alexg]]></dc:creator>
		<pubDate>Fri, 24 Feb 2017 11:01:27 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[alias]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[project management]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[wp-cli]]></category>
		<guid isPermaLink="false">http://www.alexgeorgiou.gr/?p=192</guid>

					<description><![CDATA[<p>I am always aiming for a hierarchical Gruntfile.js structure that reminds me of increasingly verbose memes. And you should, too.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/">🐗 Building WordPress plugins with an increasingly verbose Gruntfile</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>When building WordPress plugins, I&#8217;m using <strong>gruntjs</strong> for all my project tooling needs. I am always aiming for a hierarchical Gruntfile.js structure that reminds me of <a href="http://knowyourmeme.com/memes/increasingly-verbose-memes" target="_blank" rel="noopener noreferrer">increasingly verbose memes</a>. And you should, too.</p>
<p>No matter which tool you&#8217;re using, building, testing, packaging and deploying a WordPress plugin should be one command. That way when you&#8217;re dev testing, you are actually testing the whole process, including installation, activation and deactivation of the plugin&#8217;s zip file. <a href="https://github.com/sindresorhus/grunt-shell" target="_blank" rel="noopener noreferrer">grunt-shell</a> and <a href="http://wp-cli.org/" target="_blank" rel="noopener noreferrer">wp-cli</a> are your friends. Use <a href="http://wp-cli.org/commands/option/" target="_blank" rel="noopener noreferrer"><strong>wp option</strong></a> liberally in your dev deployment code.</p>
<p>My Gruntfile.js describes all the steps needed between my code sitting in my working directory and the same code running in my WordPress dev environment.</p>
<p><a href="http://gruntjs.com/creating-tasks" target="_blank" rel="noopener noreferrer">Grunt alias tasks</a> let you define this process hierarchically. Use them! I aim to model the process so that at some level it has some resemblance to steps from the waterfall model: <strong>build</strong>, <strong>package</strong>, <strong>test</strong>, and <strong>deploy</strong> are verbs that I always aim to define in my script. Then, at a lower level I can define which grunt plugins actually accomplish each step.</p>
<p>When you have defined the steps in this way, then you can begin to create builds of varying quality or for various purposes. Testing takes too long to run every time in your development cycle? Create a quick build that excludes it. Want to have separate builds with and without documentation included? Define another package verb. Want to create a deliverable package but not deploy it? Create a task that omits the deploy verb.</p>
<p>The following is from an actual project I&#8217;m working on. Don&#8217;t worry about the details. You have your own workflow. All I&#8217;m saying is that you will benefit from structuring your script hierarchically.</p>
<p>Here&#8217;s my increasingly verbose meme. Make it viral!</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-193" src="http://www.alexgeorgiou.gr/wp-content/uploads/2017/02/grunt-meme.jpeg" alt="Increasingly verbose Gruntfile" width="640" height="800" srcset="https://www.alexgeorgiou.gr/wp-content/uploads/2017/02/grunt-meme.jpeg 640w, https://www.alexgeorgiou.gr/wp-content/uploads/2017/02/grunt-meme-240x300.jpeg 240w" sizes="(max-width: 599px) calc(100vw - 50px), (max-width: 767px) calc(100vw - 70px), (max-width: 991px) 429px, (max-width: 1199px) 637px, 354px" /></p>
<p>The post <a href="https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/">🐗 Building WordPress plugins with an increasingly verbose Gruntfile</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.alexgeorgiou.gr/increasingly-verbose-gruntfile/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Developing and maintaining &#8220;freemium&#8221; WordPress plugins</title>
		<link>https://www.alexgeorgiou.gr/developing-maintaining-freemium-wordpress-plugins/</link>
					<comments>https://www.alexgeorgiou.gr/developing-maintaining-freemium-wordpress-plugins/#respond</comments>
		
		<dc:creator><![CDATA[alexg]]></dc:creator>
		<pubDate>Mon, 30 May 2016 14:55:02 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[freemium]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[monetization]]></category>
		<category><![CDATA[wordpress]]></category>
		<guid isPermaLink="false">http://www.alexgeorgiou.gr/?p=86</guid>

					<description><![CDATA[<p>Here I would like to share with you my strategy for developing and maintaining "freemium" WordPress plugins that are easy to maintain and release.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/developing-maintaining-freemium-wordpress-plugins/">Developing and maintaining &#8220;freemium&#8221; WordPress plugins</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Here I would like to share with you my strategy for developing and maintaining &#8220;freemium&#8221; WordPress plugins that are easy to maintain and release.</p>
<h1>Monetizing WordPress software</h1>
<p>WordPress is such a fun platform to work with not so much for its core features, but due to its vibrant ecosystem of themes and plugins.</p>
<p>On <a href="https://wordpress.org/">WordPress.org</a> there&#8217;s a ton of <strong>high-quality software that you can use for free</strong> (they&#8217;re all <a href="https://opensource.org/licenses/gpl-license">GPL</a>), and they cater to seemingly every need you can think of. This is perhaps the software channel with the largest exposure since it is integrated into the WordPress admin screen.</p>
<p><a href="https://envato.com/">Envato</a> is a very successful <strong>software market</strong> where developers can charge money for their themes and plugins, but there are strict rules for admission and the distribution channel does the pricing; not the developer. The channel also takes a huge cut which currently ranges from 50 to 70 percent depending on whether you give them exclusive rights over your creation.</p>
<p>Or you can buy access to a whole bunch of plugins from developer teams that have put a lot of stuff out there, such as <a href="http://wpmudev.org">WPMU DEV</a>. Even factoring in the support and documentation you get, the prices you pay are very low.</p>
<p>Very few plugins will cost you a price higher that a two digit amount of US dollars. So who makes these free, or dirt-cheap themes and plugins, and how do they get paid for their work?</p>
<p>Just do a google search on <strong>WordPress and business models</strong>, and you will come across articles from developers who discuss their experience on how they monetize on their work. I feel that this article sums it up nicely:</p>
<p class="wpview-selection-before">
<p class="wpview-selection-after">
<p>So it turns out there are a bunch of way to make money selling your WordPress software. Today I&#8217;m going to focus on the simplest and perhaps most popular way:</p>
<h1>The Freemium model</h1>
<p>It&#8217;s all a numbers game. Sure, you might be selling your plugin for only a few bucks, but if a million people buy it, you&#8217;re rich. After all, <a href="http://trends.builtwith.com/cms/WordPress">a very very large percentage of all websites on the Internet run WordPress</a>. That looks like a huge market. Surely even the slimest slice from that pie should be big enough by your standards?</p>
<p>Not so fast, though. How feasible is it to reach large numbers of people? Having built a good and robust product, on its own, is a good start, but you need to do more. You have to think of how to reach your customers, and how you will convince them to:</p>
<ol>
<li>look at your product,</li>
<li>decide to use your product, and</li>
<li>reach into their pockets and pay you for your hard work.</li>
</ol>
<p>Think web marketing and promotion. Think exposure to the right channels. Think social media. Think landing pages and variable pricing, and promo codes and &#8220;growth hacking&#8221; and all that good stuff. How you go from having a product to having money in the bank is your business model. Your strategy. The <strong>Freemium</strong> model is a tried and true model in the WordPress world.</p>
<p>A portmanteau of &#8220;Free&#8221; and &#8220;Premium&#8221;, this model is what it says on the title: You build a product that is high-quality and fully functional. If it solves a real need, with the right kind of promotion you will have a chance of reaching large numbers of users. That&#8217;s step 1.</p>
<p>Don&#8217;t rush into asking for money at this stage. Nobody will be willing to pay for your product at first. Not even a dollar. Website developers and administrators these days are pandered with absolutely too many high-quality freebies out there. It&#8217;s a large Internet and competition is fierce.</p>
<p>At least, they won&#8217;t pay with money. But if they choose to give your free product a try, they will have to pay with their valuable <em>time</em>. Even the easiest piece of software takes you a few minutes to install, configure and figure out. You need to convince your potential customer that what they get for free is worth 5 or 10 minutes of their time, or they will spend those 10 minutes trying out someone else&#8217;s product.</p>
<p>Once you&#8217;ve done all that, you will begin to build a user base. Now it is out of that user base that you can begin to hope that a small percentage will maybe pay a small amount. These dedicated users of your community will pay for everybody else&#8217;s free ride.</p>
<h1>Source code maintenance</h1>
<p>So you have a <strong>free edition</strong> of your product, plus a <strong>premium edition</strong> that does all the stuff that the free does, plus a few more features. These features are not absolutely necessary for beginers, but that will come in handy to a few of your most dedicated users. You will have to put the free edition on software channels with high exposure, create some noise around them, and put the premium edition behind some kind of paywall or restricted area. In that same area you might offer support and additional freebies, and perhaps even a community for your users.</p>
<p>How do you maintain this? Surely you don&#8217;t want to have two copies of your source tree. That&#8217;s a maintainer&#8217;s nightmare. <strong>You don&#8217;t want to duplicate any code. But you do want to have variations between the two release types.</strong> Perhaps there is some placeholder text or popup in the free version where the premium feature is missing. You will want to add links to a landing page where users can purchase your premium version. <strong>In essence you have two products that are similar in some ways, but not exactly the same.</strong> And users who have the free edition installed might be baffled if they buy and install the premium edition and it clashes with their free installation, due to some namespace clash.</p>
<p><strong>The solution that I prefer the best is to use a source code preprocessor. </strong>Preprocessors are tools that do stuff with your source before it runs or gets packaged. Let&#8217;s look at your requirements and how a preprocessor meets them:</p>
<ul>
<li>You want to be able to <strong>include files conditionally</strong> into your deliverables. Perhaps this or that directory only goes into the Premium edition.</li>
<li>File-level granularity will probably not cut it on its own, though. You will have large files where only a few lines will change between editions. You want to be able to say &#8220;if i&#8217;m building the free edition, include this code segment in the build, else include that code&#8221;. So you need to be able to use <strong>inline conditionals </strong>into your code.</li>
<li>You want to be able to <strong>insert variables</strong> into your code that are different for each of your multiple editions. Your welcome message might be something along the lines of &#8220;thanks for downloading xyz free&#8221; versus &#8220;thanks for purchasing xyz premium&#8221;. Keeping the title of xyz in a preprocessor variable will com handy.</li>
<li>Also remember that you will not only need to do this in PHP code. Think CSS and JavaScript. In my case, I produce documentation with Markdown. So you need something that works well <strong>across multiple languages</strong>.</li>
</ul>
<p>Readers of this blog will know how much I love <a href="http://gruntjs.com/">Grunt</a>. Grunt is something that in my opinion you should already be using in your project for all kinds of automation. I guess you could get away with plain old shell scripting or some other build tool, but you will definitely want to automate some tasks such as testing, building, producing documentation, packaging and releasing.</p>
<p>I have split my project into the following directories which represent the <strong>workflow from source to deliverables</strong>:</p>
<ul>
<li><code>/src</code> — I keep my source code here. This is just an assortment of PHP, JavaScript and CSS magic, as well as a few binary assets. The codes are sprinkled with preprocessor directives that <a href="https://github.com/jsoverson/grunt-preprocess"><code>grunt-preprocess</code></a> will understand. This is a grunt plugin that meets the aforementioned requirements very well.</li>
<li><code>/doc</code> — Here I keep my documentation in Markdown format. Read <a href="http://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/">this other article I wrote on how I use grunt and pandoc to convert it into PDFs</a>. Again, there is very little duplication here thanks to preprocessor directives.</li>
<li><code>/build/free</code> and <code>/build/premium</code> — This is where the output of the preprocessor dumps the processed code and documentation. Some generated data files that contain lists of fonts are dumped here too. I use <a href="https://github.com/sindresorhus/grunt-shell"><code>grunt-shell</code></a> to run arbitrary linux commands and write the output here. I keep these to a minimum, since the preprocessor does a lot of the dynamic stuff. I also dump here my translation files.</li>
<li><code>/package/free</code> and <code>/package/premium</code> — This is where the source code is zipped. I use <a href="https://github.com/gruntjs/grunt-contrib-compress"><code>grunt-contrib-compress</code></a> to produce a WordPress plugin in installable <code>.zip</code> form. Also, <code>pandoc</code> dumps the PDF documentation here.</li>
<li><code>/deliverables</code> — You might think that the product has been through enough steps by now, but you&#8217;d be wrong. The end deliverable is more than just code. So finally I have grunt-contrib-compress zip up the plugin&#8217;s .zip file and the PDFs into a final .zip file, once for each edition, free and premium.</li>
</ul>
<p>Simply typing <code>grunt</code> on my command line will test and do all of the above. It will also use <a href="http://wp-cli.org/">wp-cli</a> to install and activate the plugins into my development environment so I can manually test.</p>
<h1>Deployment considerations</h1>
<p>I&#8217;m going to leave you with <strong>some code that makes sure not both editions are activated</strong> in your freemium plugin. It showcases the <a href="https://codex.wordpress.org/Function_Reference/deactivate_plugins">deactivate_plugins()</a> function and at the same time gives you <strong>a taste of what it feels like to use preprocessor directives</strong> in your code. Enjoy!</p>
<p>&nbsp;</p>
<pre>&lt;?php
/*
 * Plugin Name: // @echo name
 * Description: // @echo description
 * Version: // @echo version
 * Plugin URI: // @echo homepage
 * Author: // @echo author
 * Author URI: http://alexgeorgiou.gr
 * Text Domain: myplugin
 * Domain Path: /languages/
// @ifdef FREE
 * License: GPLv2 or later
// @endif
 *
 * @package myplugin
 * @since 1.0.0
 */

// don't load directly
defined( 'ABSPATH' ) || die( '-1' );

if ( ! class_exists( '/* @echo class */' ) ) {

    final class /* @echo class */ {

        // @ifdef PREMIUM
        private $_disabled_notice = false;

        private $_must_disable_notice = false;
        // @endif

        private function __clone() {
            // Cloning disabled
        }

        private function __construct() {
            set_error_handler( array( __CLASS__, 'error_handler' ) );
            
            // more stuff here

            // @ifdef PREMIUM
            add_action( 'admin_init', array( &amp;$this, 'action_admin_init' ) );
            add_action( 'admin_notices', array( &amp;$this, 'action_admin_notices' ) );
            // @endif

            // more stuff here

            restore_error_handler();
        }

        // @ifdef PREMIUM
        
        /**
         * Makes sure that free and premium editions are not both installed.
         */
        public function action_init() {
            set_error_handler( array( __CLASS__, 'error_handler' ) );

            // Disable free if free and premium are both installed
            if ( class_exists( '/* @echo pkg_name */_premium' ) &amp;&amp; class_exists( '/* @echo pkg_name */_free' ) ) {
                if ( current_user_can( 'activate_plugins' ) &amp;&amp; function_exists( 'deactivate_plugins' ) ) {
                    $plugin = plugin_basename( __FILE__ );
                    // @ifdef PREMIUM
                    $plugin = str_replace('myplugin-premium/', 'myplugin/', $plugin);
                    // @endif
                    deactivate_plugins( $plugin );
                    $this-&gt;_disabled_notice = true;
                } else {
                    $this-&gt;_must_disable_notice = true;
                }
            }

            restore_error_handler();
        }

        public function action_admin_notices() {
            set_error_handler( array( __CLASS__, 'error_handler' ) );

            if ( $this-&gt;_disabled_notice ) {
                echo '&lt;div class="notice notice-warn is-dismissible"&gt;&lt;p style=\"font-size: larger;"&gt;';
                echo '&lt;strong&gt;' . str_replace( '_', ' ', __CLASS__ ) . ':&lt;/strong&gt; ';
                echo __( 'Thank you for installing the premium edition of this plugin. ' );
                echo __( 'The free edition has been deactivated.' );
                echo '&lt;/p&gt;&lt;/div&gt;';
            }

            if ( $this-&gt;_must_disable_notice ) {
                echo '&lt;div class="notice notice-error is-dismissible"&gt;&lt;p style=\"font-size: larger;"&gt;';
                echo '&lt;strong&gt;' . str_replace( '_', ' ', __CLASS__ ) . ':&lt;/strong&gt; ';
                echo __( 'Thank you for installing the premium edition of this plugin. ' );
                echo __( 'The free edition of the plugin must be deactivated, but your account lacks the necessary priviledges. Please ask the site administrator to disable it, or delete the directory &lt;code&gt;wp-content/plugins/myplugin&lt;/code&gt; using FTP or some other means.' );
                echo '&lt;/p&gt;&lt;/div&gt;';
            }
            restore_error_handler();
    }
    // @endif
}</pre>
<p>The post <a href="https://www.alexgeorgiou.gr/developing-maintaining-freemium-wordpress-plugins/">Developing and maintaining &#8220;freemium&#8221; WordPress plugins</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.alexgeorgiou.gr/developing-maintaining-freemium-wordpress-plugins/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Create PDF documentation like a pro</title>
		<link>https://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/</link>
					<comments>https://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/#comments</comments>
		
		<dc:creator><![CDATA[alexg]]></dc:creator>
		<pubDate>Tue, 10 May 2016 14:35:31 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[documentation]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[latex]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[pandoc]]></category>
		<category><![CDATA[pdf]]></category>
		<guid isPermaLink="false">http://www.alexgeorgiou.gr/?p=60</guid>

					<description><![CDATA[<p>In this article I will show you how you can use a mixture of Markdown, LaTeX, and inline JavaScript to create PDF documents as part of your Grunt project.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/">Create PDF documentation like a pro</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><strong>In this article I will show you how you can use a mixture of <a href="https://daringfireball.net/projects/markdown/">Markdown</a>, <a href="https://latex-project.org/">LaTeX</a>, and JavaScript to create <a href="https://en.wikipedia.org/wiki/Portable_Document_Format">PDF</a> documents as part of your Grunt project. </strong>There are several advantages to this approach:</p>
<ul>
<li>Text-based documents are much more <strong>friendly to revision control systems</strong> than, say, Microsoft Word documents or even LibreOffice Writer documents.</li>
<li><strong>Markdown is convenient.</strong> 90% of the time it is all you need.</li>
<li><strong>LaTeX is powerful.</strong> You can use it for that other 10% of the stuff you want to do. But you don&#8217;t want it to get in the way every time you start a new section or add a hyperlink.</li>
<li><strong>JavaScript is even more powerful.</strong> Thanks to <code>grunt</code> you can link variables from your project&#8217;s metadata into your text. Think names and version numbers. When your project&#8217;s metadata change, your documents reflects this. If this is not cool, I don&#8217;t know what is.</li>
<li><strong>All of this can be nicely integrated to your build process.</strong> I will show you how I did this using <code>grunt</code>, <code>grunt-contrib-watch</code>, <code>grunt-panda</code>, and a dual monitor PC for a near-WYSIWYG authoring experience. Read on!</li>
</ul>
<h2>RTFM in the third millennium?</h2>
<p><strong>Generally speaking, software documentation is a thing of the past. </strong>Most end-user software does not come with instructions any more. The reason? Most end-users do not <em>read</em> the instructions. (This is why engineers had to come up with the acronym RTFM. But users have their own acronym: TL;DR!) So the fact remains that users don&#8217;t read instructions, even if they are right there on the screen, let alone in a separate PDF file on their computer. If the UI is not intuitive enough for them to figure out how to do something, users will do one of the following:</p>
<ol>
<li><strong>Switch to some user-friendlier piece of software.</strong></li>
<li><strong>Ask their children or grandchildren to figure out how to do it.</strong> Depending on availability.</li>
<li><strong>If all else fails, they might go ask a question in a user community</strong> such as a forum, issue tracker, comment section, twitter, etc. This is the ideal user.</li>
</ol>
<blockquote><p>Users will crawl on broken glass to avoid intentionally learning something new. Users will never read your documentation, not even at gunpoint.</p></blockquote>
<p><strong>But there still are, and always will be, cases when you do need to produce documentation for your software.</strong> For instance, your intended audience might be technically minded. Or you might be explicitly <em>required</em> to produce a User&#8217;s Manual. Envato for instance is a platform that asks from its contributors to include a manual with themes and plugins. This makes sense, because users of themes and plugins are often technically minded themselves. They might even find it welcome to know that if all else fails they can dive into a manual and find the solution to their problem. It&#8217;s all a matter of perspective.</p>
<h2>How pros create PDFs</h2>
<p><strong>Traditionally, whenever they dabble in typography, computer geeks such as ourselves have been using LaTeX,</strong> an evolution of Donald Knuth&#8217;s TeX. In addition to the immense power of TeX, there is also the advantage of the esoteric syntax which makes you feel like a l33t h@xor when you use it. In fact the language is so complex that there is an entire stackexchange QA site devoted to it: <a href="http://tex.stackexchange.com/">http://tex.stackexchange.com/</a>.</p>
<p>Purists might argue that easier is better. Bah! As a geek who enjoys assembly code and Perl, I tend to disagree, but I see their point: Pressing deadlines leave little time for you to play with TeX when fancy word processors are at your fingertips. But as a programmer you really don&#8217;t want to be using binary files in your revision control system. They don&#8217;t diff well. Hence people nowadays use Markdown, which is very easy to write and can readily be transformed to HTML, PDF, etc. albeit at a loss of expressive power.</p>
<p>If you enjoy such handy features as unicode support and using your own fonts, you might decide to use <a href="https://www.sharelatex.com/learn/XeLaTeX">XeTeX</a> and its XeLaTeX compiler instead of LaTeX.</p>
<h2>pandoc</h2>
<p>Enter <a href="http://pandoc.org/">pandoc</a>. Pandoc is really cool, so you&#8217;ll want to install it:</p>
<pre>sudo apt-get install pandoc</pre>
<p>Here&#8217;s how you&#8217;d convert your markdown document to a PDF using XeTeX:</p>
<pre>pandoc readme.md -o readme.pdf --latex-engine=xelatex</pre>
<p>Easy. Pandoc silently converts the markdown to LaTeX and then spares you the usual complexity of converting LaTeX to DVI, DVI to PS, and PS to PDF. In goes markdown, out comes PDF. Magic!</p>
<h2>grunt</h2>
<p>Since pandoc is a thing that exists in this world, naturally there must be a grunt plugin for it, right? Correct. It is called <a href="https://github.com/gmp26/grunt-panda">panda</a>. You&#8217;ll need to install it in your grunt project as a dev dependency with</p>
<pre>npm install grunt-panda --save-dev</pre>
<p>and then also load it in your Gruntfile.js:</p>
<pre>grunt.loadNpmTasks( 'grunt-panda' );</pre>
<p>You&#8217;ll want to read the panda README for details, but this is what one might do to convert a markdown document into PDF:</p>
<pre> panda: {
   doc: {
     options: {
       process: true,
       pandocOptions: "--latex-engine=xelatex"
     },
     files: {
       'build/readme.pdf': [ 'docs/readme.md' ],
     }
   }
 },</pre>
<p>If you want to control your LaTeX headers or other document metadata, you can prepend your markdown code with <a href="http://pandoc.org/README.html#epub-metadata">YAML headers</a>.</p>
<pre>---
title: '**&lt;%= pkg.name.replace(/_/g,' ') %&gt;** **&lt;%= pkg.version %&gt;** user manual'
author: 
- name: '&lt;%= pkg.author %&gt;'
header-includes: |
 \setmainfont{Arial}
abstract: |
 This paragraph is an abstract that briefly talks about this software manual.
---

= Introduction

_Hello_ PDF world!</pre>
<p>Check out how I defined the title of the document and author name using grunt-flavored JavaScript. The values come from my <code>package.json</code> file. All of this is possible thanks to panda&#8217;s option <code>process: true</code>. Since JavaScript is turing-complete, you could potentially go full meta in there and generate chunks of your document dynamically. Please don&#8217;t do that! :-p Here I just replace underscores with spaces in the <code>pkg.name</code> field.</p>
<p>You can go on and use LaTeX syntax, Markdown syntax and <code>&lt;%= %&gt;</code> tags throughout the document. The only downside is that if you do something really really funky, panda might fail silently. In that case, simply attempt to convert the file manually using pandoc, as we saw before. You will then get error messages from the xelatex engine that will point you in the right direction.</p>
<h2>WYSIWYG</h2>
<p>Once you&#8217;re satisfied that calling <code>grunt panda</code> does indeed generate your documents, why not sprinkle some <code>grunt-contrib-watch</code> on top of it all?</p>
<pre> watch: {
   docs: {
     files: [ 'docs/**/*.md' ],
     tasks: [ 'panda' ],
     options: {
       spawn: false,
     },
   },
 },</pre>
<p>Aaah nice. Now whenever you save changes to your markdown, the PDF is regenerated. If you&#8217;re on Linux, I recommend that you open the PDF with <a href="http://www.gnome.org/projects/evince">Evince</a>, which is usually the default PDF viewer in Gnome desktops. It has the nice feature that it auto-reloads a PDF that has changed on disk. Slide the window to your second monitor, and leave <code>grunt watch</code> running. Now you can edit your Markdown/LaTeX/JavaScript source and see the result almost in real time.</p>
<p>The post <a href="https://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/">Create PDF documentation like a pro</a> appeared first on <a href="https://www.alexgeorgiou.gr">Alexandros Georgiou</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.alexgeorgiou.gr/create-pdf-documentation-like-pro/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
