Finds where are arbitrary templating marker heads and tails located

Quick Take

import { strict as assert } from "assert";
import { strFindHeadsTails } from "string-find-heads-tails";

// processing an arbitrary, custom templating markup:
    "some text %%_var1-%% more text %%_var2_%%",
    ["%%_", "%%-"], // two flavours of heads
    ["-%%", "_%%"] // two flavours of tails
      headsStartAt: 10,
      headsEndAt: 13,
      tailsStartAt: 17,
      tailsEndAt: 20,
      headsStartAt: 31,
      headsEndAt: 34,
      tailsStartAt: 38,
      tailsEndAt: 41,


There are many templating languages out there, each using different special "markers" — Nunjucks use {{ and }}, Salesforce use {! and }, Mailchimp use *| and |* — there are many templating languages.

We call those "markers" heads and tails (invented term) because we need to distinguish between the two.

This program finds out, where are the templating marker heads and tails located in a given string.

It will be used in JSON pre-processing and it will let you use any existing or invented templating language.


It's a (scanerless) parser for arbitrary templating language markers.

There are few rules:

  • Each finding must be in sequence: heads - tails - heads - tails.
  • When one heads is found, no new heads findings will be accepted into the results until there's a new tails finding. Same goes the opposite way, for tails.
  • Both heads and tails can be supplied either as a single string or array of strings. Findings are prioritised by their order in the array.



In other words, it's a function which takes four input arguments, last-one optional (marked with square brackets).

You can switch to grapheme count-based index system — use nativeToUnicode() method of string-convert-indexes. It can process the whole output of this library.

API - Input

Input argument Type Obligatory? Description
str String yes The string in which you want to perform a search
heads String or Array of strings yes One or more string, the first half of the set. For example, ['%%-', '%%_'].
tails String or Array of strings yes One or more string, the second half of the set. For example, ['-%%', '_%%'].
opts Plain object no An Optional Options Object. See its API below.

PS. Input arguments are not mutated.

Optional Options Object

options object's key Type of its value Default Description
fromIndex Natural number or zero as number or string 0 If you want to start the search later, only from a certain index, set it here. Same as 2nd argument position in String.includes.
throwWhenSomethingWrongIsDetected Boolean true By default, if anything wrong is detected, error will be thrown. For example, tails precede heads. Or two conescutive heads or tails are detected. If you want to turn this functionality off, set to false. Turning this off automatically sets the allowWholeValueToBeOnlyHeadsOrTails (see below) to true, that is, error won't be thrown when whole input is equal to one of heads or tails. This setting does not concern wrong input types. To allow input in wrong types, set relaxedAPI, see below.
allowWholeValueToBeOnlyHeadsOrTails Boolean true If whole input str is equal to one of heads or tails AND opts.throwWhenSomethingWrongIsDetected is true, THEN error won't be thrown and that input will not be processed. But if you set this to false AND error throwing is on (opts.throwWhenSomethingWrongIsDetected is true), error will be thrown. This feature is activated only when opts.throwWhenSomethingWrongIsDetected is true.
source String string-find-heads-tails Packages that consume this package as a dependency might rely on some of our error throwing functionality. Since thrown message mentions the name of the throwee, you can override it, setting to parent package's name.
matchHeadsAndTailsStrictlyInPairsByTheirOrder Boolean false If it's set to true, the index numbers of heads and tails in their input arrays must match. Different pairs can have different indexes, as long as they match between the pair. For example, %%_test-%% or %%-test_%%.
relaxedAPI Boolean false If it's set to true, wrong inputs will instantly yield []. If it's default setting, false, it would throw an error. This only concerns the checks before any real work is done on the input, where error-throwing is controlled by throwWhenSomethingWrongIsDetected (see above).

Here is the Optional Options Object in one place with all default settings:

fromIndex: 0,
throwWhenSomethingWrongIsDetected: true,
allowWholeValueToBeOnlyHeadsOrTails: true,
source: 'string-find-heads-tails',
matchHeadsAndTailsStrictlyInPairsByTheirOrder: false,
relaxedAPI: false

API - Output

Returns an array of zero or more plain objects, each having format:

headsStartAt: 1,
headsEndAt: 2,
tailsStartAt: 4,
tailsEndAt: 5,

The whole idea is that you should be able to get the heads if you put str.slice(headsStartAt, headsEndAt).

If you want to use grapheme-count-based indexing, first convert the output of this library using string-convert-indexes, then use Unicode-character-count-based string slice libraries, for example: string-slice opens in a new tab.


See it in the monorepo opens in a new tab, on GitHub.


To report bugs or request features or assistance, raise an issue on GitHub opens in a new tab.

Any code contributions welcome! All Pull Requests will be dealt promptly.


MIT opens in a new tab

Copyright © 2010–2021 Roy Revelt and other contributors

Related packages:

📦 json-variables 11.0.1
Resolves custom-marked, cross-referenced paths in parsed JSON
📦 detergent 8.0.1
Extracts, cleans and encodes text
📦 string-strip-html 9.0.1
Strips HTML tags from strings. No parser, accepts mixed sources
📦 string-apostrophes 2.0.1
Comprehensive, HTML-entities-aware tool to typographically-correct the apostrophes and single/double quotes
📦 string-collapse-leading-whitespace 6.0.1
Collapse the leading and trailing whitespace of a string
📦 string-convert-indexes 5.0.1
Convert between native JS string character indexes and grapheme-count-based indexes
📦 string-extract-sass-vars 3.0.1
Parse SASS variables file into a plain object of CSS key-value pairs