All notable changes to this project will be documented in this file.
See Conventional Commits opens in a new tab for commit guidelines.


1 Dec 2022


  • Minimum supported Node version is v14.18; we’re dropping v12 support


12 Aug 2022



18 Apr 2022

🔧 Fixed


9 Sept 2021



  • programs now are in ES Modules and won’t work with Common JS require()


24 May 2021


  • config file based major bump blacklisting (e15f9bb)


11 Apr 2021


  • Revert “chore: setup refresh” (23cf206)


28 Jan 2021

🔧 Fixed

  • add testStats to npmignore (f3c84e9)


23 Jan 2021


  • rewrite in TS, start using named exports (7c3419c)


  • previously you’d consume like: import jVar from … — now: import { jVar } from …


28 Nov 2020

Accidental version bump during migration to SourceHut. Sorry about that.


19 Apr 2020


  • opts.allowUnresolved can be supplied as string (ada2a42) — thanks to Thomas Ivall


18 Apr 2020


  • opts.allowUnresolved — boolean flag allows to resolve to optionally empty string instead of throwing — thanks to Thomas Ivall
  • some rebasing, two dependencies removed (05e9346)


2 Oct 2019

Performance Improvements

  • remove check-types-mini which gaves 6x times speed gain (21a1cb0)


20 Jan 2019
  • Various documentation and setup tweaks after we migrated to monorepo
  • Setup refresh: updated dependencies and all config files using automated tools


28 Oct 2018
  • Updated all dependencies
  • Restored unit test coverage tracking: reporting in terminal and
  • Restored unit test linting


20 Jun 2018


10 Jun 2018
  • Migrated to BitBucket (to host repo + perform CI) and Codacy (for code quality audit)
  • Dropped BitHound (RIP) and Travis
  • Remove package-lock


28 Mar 2018
  • Fixed one edge case where variables were resolving to non-strings and error was thrown.
  • Set up prettier and removed .editorconfig.


6 Mar 2018
  • 🔧 Updated all dependencies again. I was/am tinkering with string-match-left-right and its previous version could have caused throws on certain cases. Sorry about that. Big features are coming soon for compensation 😋.


16 Feb 2018
  • Updated all dependencies.
  • Fixed some edge-cases where variables could have been wrapped where they shouldn’t have been wrapped and the opposite.


14 Feb 2018
  • Full rewrite. Same and more unit tests.
  • Variables now can be fetched from the parent nodes, all the way up until root. Priority order: 1. key with variable’s name at the same level; 2. data store at same level; 3. key with variable’s name at parent node’s level; 4. data store at parent’s level … and so on, until root is reached.
  • Now the program will pretty much never throw. If you feed incomplete heads or tails, it will leave them unouched rather than throw an error about them. I’ll implement strict mode later.
  • Way leaner and efficient algorithm and dependencies’ choice.
  • Added more unit tests too.


2 Jan 2018

Small but nonetheless breaking changes.

✈️ Changes

  • 💥 Mismatching wrapping/nonwrapping heads and tails are not permitted any more. Either both are wrapping or both are non-wrapping. If you used this library in normal ways you should not see the difference. The benefits of this changes are huge — 💥 We have completely rehauled head and tail detection (see below) and accepting only matching pairs allows us to identify more false-positives.

PS. Bumping semver major just in case (could have bumped minor) but let’s better be safe than sorry 😉

🏗️ Improvements

  • Improved head and tail detection algorithm. Previously we used simple string search, without considering the order of the findings. Wrong order now will help to rule-out more false positives.
  • Tapped ast-monkey-traverse directly, without the need for the whole ast-monkey.
  • Many other improvements on the setup


15 Dec 2017
  • Rebased in ES Modules
  • Set up Rollup to generate three builds: CommonJS, UMD and ES Modules
  • Dropped JS Standard and tapped raw ESLint on airbnb-base preset, with an override to ban semicolons


29 Jul 2017
  • Replaced object-assign with ES6 Object.assign


23 May 2017
  • Separated the arrayiffy-if-string into a standalone library and tapped it.


20 May 2017
  • Updated all deps
  • Updated Codsen name


15 May 2017


5 May 2017
  • opts.resolveToFalseIfAnyValuesContainBool (on by default)

  • opts.throwWhenNonStringInsertedInString (off by default)

  • Now allowing to query deeper-level values. For example:

      a: “some text %%_b.key2_%% more text”,
      b: {
        key1: “val1″,
        key2: “val2″,
        key3: “val3″,
    // => {
    //      a: ‘some text val2 more text’,
    //      b: {
    //        key1: ‘val1′,
    //        key2: ‘val2′,
    //        key3: ‘val3′
    //      }
    //    }
  • Obviously, the new changes above threw the opts.dontWrapVars out of track a little bit since instead of key now we’ve possibly got key.key[element.key], so I fixed that too and added more unit tests.

Blimey, we’ve got 99 unit tests! 🍾 We’ll need to celebrate the 100th! 🍻✨


3 May 2017
  • Variables are not correctly resolved from linking to the same (deeper) level. If not found, search continues in that level’s data store key (default key name for data stores is <same-key-name>_data). If not found there, search jumps to root level, and looks for key there. If not found, search continues for data store at the root. If failed, error is thrown.
  • Some rebasing done to clean things up 🚿.


30 Apr 2017

Breaking changes in the API, or rather output. When Boolean values are encountered and written as values (and no other string characters are present on the original placeholder), we output empty string, not false or true. Nobody needs Booleans converted to String. I need this for my email templating projects. 🦄

  • JSON specs allow various kinds of types to be placed as key values: Booleans, arrays, plain objects and even null. In this release we loosen the restrictions and allow these different types to be resolved as values, particularly, null and Booleans.

  • Added opts.resolveToBoolIfAnyValuesContainBool — if true (default), if any variable’s value is Boolean, upon resolving it will set the whole value to itself. For example, aaaa %%_var_%% bbbbb and var=true would resolve to true. If setting is false, it will resolve to empty string, in this example’s case, aaaa bbbbb. I don’t see why anybody would set it to other value than default, but hey, the more freedom the better.

  • opts.resolveToFalseIfAnyValuesContainBool — if Boolean variable is enountered, the whole thing always resolves to false. Even if the first encountered variable’s value was true. This is needed for email templating, so that when Booleans are encountered, we bail setting the whole thing to false.


27 Apr 2017
  • opts.headsNoWrap (default %%-) & opts.tailsNoWrap (default -%%). When the same string has multiple occasions of a variable and we want some variables to be wrapped but some not it was previously impossible. Wrapping ignores were global. Now not. I came with an idea to allow different (customiseable) heads and tails which prevent wrapping on the variable it marks.
  • 💥 opts.wrapHeads is now opts.wrapHeadsWith
  • 💥 opts.wrapTails is now opts.wrapTailsWith


25 Apr 2017
  • 💥 Breaking changes & major semver version bump: opts.dontWrapVarsStartingWith and opts.dontWrapVarsEndingWith merged into one and allow wildcards now. It’s way more powerful since you can glob not only the starting/ending pieces of string but anything within it. You can put a wildcard in the middle now or even multiple wildcards!


20 Apr 2017
  • If any key’s value contains only a heads or tails marker and nothing else, it will not throw. You can force throwing (that’s previous version’s behaviour) setting opts.noSingleMarkers to true. But the default is false.
  • Did some code rebasing.


6 Apr 2017
  • Fixed one edge case where the source was array, it was querying variable from _data key store, which was in turn querying variable from its key data store.
  • More tests to keep coverage at 100%


28 Mar 2017
  • First public release