npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@codejamboree/replace-tags

v1.2.2

Published

Effortlessly replace placeholders with dynamic content using this powerful text manipulation toolkit.

Downloads

112

Readme

@codejamboree/replace-tags

A lightweight utility for replacing tags in a text string with values from an object.

Replaces {{tag.paths}} in templates by default, with many styles available and customizable.

Installation

You can install the package via npm:

npm install @codejamboree/replace-tags

Or using yarn:

yarn add @codejamboree/replace-tags

Or using a CDN (Content Delivery Network)

<script src="https://cdn.jsdelivr.net/npm/@codejamboree/[email protected]/dist/index.min.js"></script>

Usage

const { replaceTags } = require("@codejamboree/replace-tags");

// Define your text containing tags
const text = "Hello {{user.name}}, welcome to {{website}}!";

// Define an object with values to replace the tags
const values = {
  user: {
    name: "John Doe",
  },
  website: "example.com",
};

// Replace the tags in the text with values from the object
const replacedText = replaceTags(text, values);

console.log(replacedText);
// Output: Hello John Doe, welcome to example.com!

Via CDN

<script src="https://cdn.jsdelivr.net/npm/@codejamboree/[email protected]/dist/index.min.js"></script>
<script>
  var replaceTags = window["@codejamboree/replace-tags"].replaceTags;
  var text = replaceTags("Hello {{name}}!", { name: "World" });
  console.log(text);
  // Output: Hello World!
</script>

Configuration Interface

A page is available to try out replaceTags by changing various configuration settings and review its performance.

Unresolved Tags

If a tags property path cannot be resolved, the tag remains unchanged.

const { replaceTags } = require("@codejamboree/replace-tags");
console.log(
  replaceTags("Hello {{missing.path}}!", { user: "John Doe" }),
);
// Output: Hello {{missing.path}}!

Usage with Provided Tag Patterns

const {
  replaceTags,
  PercentSigns,
} = require("@codejamboree/replace-tags");

// Replace percent sign tags
console.log(
  replaceTags(
    "Hello %{name}%",
    {
      name: "John Doe",
    },
    PercentSigns,
  ),
);
// Output: Hello John Doe

Usage with Custom Tag Patterns

const { replaceTags } = require("@codejamboree/replace-tags");

// Define an object with values to replace the tags
const values = {
  user: {
    name: "John Doe",
  },
};

// Define custom options for tag parsing
const options = {
  // find all "tag_start-> tag.name <-tag_end"
  tagPattern: /tag_start->.*?<-tag_end/g,
  // to remove starting "tag_start->"
  tagStartPattern: /^tag_start->/,
  // to remove ending "<-tag_end"
  tagEndPattern: /<-tag_end$/,
};

// Define your text containing custom tags
const text = "Hello tag_start-> user.name <-tag_end!";

// Replace the custom tags in the text
// with values from the object using custom options
console.log(replaceTags(text, values, options));
// Output: Hello John Doe!

An error will be thrown if the following conditions are not met:

  1. The tagPattern must end with the /g flag.
  2. The tagStartPattern must begin with /^.
  3. The tagEndpattern must end with non-escaped $/.

Tag Styles

| Style | Template | | ----------------------------------- | --------------------------------- | | AngleBrackets | Hello << user.name >> | | AngleBracketsWithPercentSigns | Hello <% user.name %> | | Backticks | Hello `` user.name `` | | Chevrons | See AngleBrackets | | CurlyBraces | Hello { user.name } | | CurlyBracesWithDollarSigns | Hello {$ user.name $} | | CurlyBracesWithExclamationMarks | Hello {! user.name !} | | CurlyBracesWithHashSymbols | Hello {# user.name #} | | DollarSignsWithSquareBrackets | Hello $[ user.name ]$ | | DollarSignWithCurlyBraces | Hello ${ user.name } | | DoubleAngle | Hello « user.name » | | DoubleAtSigns | Hello @@ user.name @@ | | DoubleCaretsWithBraces | Hello ^^{ user.name }^^ | | DoubleColonsWithBraces | Hello ::{ user.name }:: | | DoubleCurlyBraces (default) | Hello {{ user.name }} | | DoubleCurlyBracesWithPercentSign | Hello {{% user.name %}} | | DoubleQuestionMarks | Hello ?? user.name ?? | | DoubleSquareBrackets | Hello [[ user.name ]] | | DoubleSquareBracketsWithDollarSigns | Hello [[$ user.name $]] | | DoubleUnderscores | Hello __ user.name __ | | Dunders | See DoubleUnderscores | | ExclamationMarks | Hello !{ user.name }! | | HandleBars | See CurlyBraces | | HashSymbolsWithCurlyBraces | Hello #{ user.name }# | | HTMLComments | Hello <!-- user.name --!> | | Mustache | See DoubleCurlyBraces | | Parentheses | Hello ( user.name ) | | PercentBrackets | See AngleBracketsWithPercentSigns | | PercentSigns | Hello %{ user.name }% | | Pointy | Hello 👉 user.name 👈 | | SquareBrackets | Hello [ user.name ] | | SquareBracketsWithColons | Hello [: user.name :] | | SquareBracketsWithHyphens | Hello [- user.name -] | | TripleCurlyBraces | Hello {{{ user.name }}} | | VerticalBars | Hello \| user.name \| |

Property Access Notation

The paths that may appear within tags allows for object traversal along the object to find the properties value in complex data structures. It can navigate through nested objects and nested arrays.

Ideally, paths can have multiple segments delimited by .. Each segment may have a part at the beginning that doesn't have a [ character, and they can end with multiple sets of brackets [ and ] before a . indicating the next segment of the path.

Since each segment and index are evaluted against object keys and arrray indexes, Unconventional paths that wouldn't work accessing properties in JavaScript will still work in the path resolution.

| values | path | unconventional | | ---------------------------------------- | -------------- | ----------------- | | {"key": "value"} | key | [key] | | ["value"] | [0] | 0 | | {"key": ["value"]} | key[0] | key.0 | | {"key": [["value"]]} | key[0][0] | key.0.0 | | {"parent": {"child": "value"}} | parent.child | parent[child] | | {"key": () => "value" } | key | [key] | | {"parent": () => ({"child": "value"})} | parent.child | [parent][child] |

The paths within the tags also allow for whitespace. You can either specify {{key}} or {{ key }} for easier readability, but you may not have whitespace within the path such as {{parent. child .name}}.

API

For detailed API documentation, pleae refer to the documentation.

replaceTags(text: string, values: object, options?: ReplaceTagsOptions): string

Replaces tags in the provided text with values from the values object.

  • text: The text string containing tags to replace.
  • values: An object or JSON object containing values to replace the tags.
  • options (optional): An object specifying options for tag parsing. Default is DoubleCurlyBraces. It can include the following properties:
    • tagPattern (optional): A regular expression pattern to find tags in the text. Default is DoubleCurlyBraces.tagPattern.
    • tagStartPattern (optional): A regular expression pattern to find the start of a tag. Default is DoubleCurlyBraces.tagStartPattern.
    • tagEndPattern (optional): A regular expression pattern to find the end of a tag. Default is DoubleCurlyBraces.tagEndPattern.
    • cache (optional): Flag indicating if values are to be cached for subsequent calls. Default is false.
    • onMissingPath (optional): A callback to resolve values for paths that found nothing. Default is to retain the tag. (path, tag) => tag

Returns the modified text string with tags replaced.

Unconventional paths

  • 0 The same as [0]
  • users.0.name The same as users[0].name
  • user[name] The same as users.name

Missing Values

If a value is not found for a given tag, the tag will remain in its original form. This behavior can be overridden to replace it with empty text, throw an error, or other custom logic by setting the onMissingPath callback. It receives both the path and tag that was unable to be resolved. The returned value is used in replacing the tag in the original template.

const { replaceTags } = require("@codejamboree/replace-tags");
const values = {};
const template = "Hello {{ this.tag.is.missing }}!";
const onMissingPath = function (path, tag) {
  console.log(path);
  console.log(tag);
  return "Unknown";
};
const options = { onMissingPath };

console.log(replaceTags(template, values, options));
// Output: this.tag.is.missing
// Output: {{ this.tag.is.missing }}
// Output: Hello Unknown!

Object Values

Any value that resolves as an object will result as that object being converted to JSON.

const { replaceTags } = require("@codejamboree/replace-tags");

const values = {
  user: {
    name: "John Doe",
  },
};
const text = "user = {{user}}";

// Replace the tags in the text with values from the object
console.log(replaceTags(text, values));
// Output: user = {"name":"John Doe"}

Dynamic Values

The replaceTags function supports dynamic behavior by allowing functions to be called during property resolution. If a value is found to be a function, it will be called. The value returned is then used to continue resolving the property path.

valueGetter(key: string, currentPath: string, fullPath: string): unknown

const { replaceTags } = require("@codejamboree/replace-tags");

const values = {
  user: {
    birthYear: 1990,
    getDecade: function (key, currentPath, fullPath) {
      if (key === "getDecade") {
        return Math.floor((this.birthYear % 100) / 10);
      } else {
        return undefined;
      }
    },
  },
};
const text = "How was the {{user.getDecade}}0's?";

// Replace the tags in the text with values from the object
console.log(replaceTags(text, values));
// Output: How was the 90's?

Cached Values

Values along a path are cached for optimization. Even when the path has differing whitespace within the tags. Tag paths used multiple times will resolve to the same value. Using functions as values demonstrates this behavior.

const { replaceTags } = require("@codejamboree/replace-tags");
let count = 0;
const getCount = () => {
  console.log("Getting Count");
  return ++count;
};
const values = { getCount };
const text = "Count 1: {{getCount}}; Count 2: {{ getCount }}";
console.log(replaceTags(text, values));
// Output: Getting Count
// Output: Count 1: 1; Count 2: 1
// Note: "Getting Count" was only logged once.

Note: Even when the cache flag is set to false for subsequent calls to replaceTags, the internal cache is still used during each individual call.

Caching Option

The cache flag may be set to true to cache resolved lookup values betweeen subsequent calls. This is optimal when reusing the same values between each call.

const {
  replaceTags,
  DoubleAngle,
} = require("@codejamboree/replace-tags");
const original = { name: "John Doe" };
const newValues = { name: "Jane Smith" };

// Replace tags
console.log(replaceTags("{{name}}", original));
// Output: John Doe

// Start caching values
console.log(replaceTags("{{name}}", newValues, { cache: true }));
// Output: Jane Smith

// Use previously resolved values
console.log(replaceTags("{{name}}", original, { cache: true }));
// Output: Jane Smith
// Actual: John Doe

// Use caching with a predefined tag style
console.log(
  replaceTags("«name»", original, {
    ...DoubleAngle,
    cache: true,
  }),
);
// Output: Jane Smith
// Actual: John Doe

console.log(replaceTags("{{name}}", original));
// Output: John Doe

Passing Values as JSON

You may pass a JSON string as the values to be evaluated.

const { replaceTags } = require("@codejamboree/replace-tags");

// Define your text containing tags
const text = "Hello {{user.name}}!";

// Define a JSON string with values to replace the tags
const values = `{"user": {"name": "John Doe"}}`;

// Replace the tags in the text with values from the object
const replacedText = replaceTags(text, values);

console.log(replacedText);
// Output: Hello John Doe!

Find Value By Path

A method is also provided for you to retrieve the resolved path values.

findValueByPath(source: object, path: string): unknown

const { findValueByPath } = require("@codejamboree/replace-tags");
console.log(
  findValueByPath({ user: { name: "John Doe" } }, "user.name"),
);
// Output: John Doe

Caching

Subsequent calls to findValueByPath will return cached values. You may use clearCache to clear the cache.

const {
  findValueByPath,
  clearCache,
} = require("@codejamboree/replace-tags");

// Define objects with values to find
const original = { name: "John Doe" };
const newValues = { name: "Jane Smith" };

// Find the value in original
console.log(findValueByPath(original, "name"));
// Output: John Doe

// Find the value in newValues
console.log(findValueByPath(newValues, "name"));
// Output: John Doe
// Actual: Jane Smith

// clear the cache
clearCache();

// Find the value in newValues
console.log(findValueByPath(newValues, "name"));
// Output: Jane Smith

Known Issues

Paired Delimiters

Tag Styles that have a paried tag with the same opening and closing tags (ie __ in __dunder__) are sometimes unable to parse tags directly following one after another. (ie __key1____key2__) It is recomended to have some whitespace between these tags. (ie __key1__ __key2__).

const {
  replaceTags,
  DoubleUnderscores,
} = require("@codejamboree/replace-tags");

// Define values to replace the tags
const values = {
  key1: "## 1 ##",
  key2: "-- 2 --",
};

// Replace the tags in the text with values from the object
console.log(
  replaceTags("__key1____key2__", values, DoubleUnderscores),
);
// Output: __key1____key2__
// Expected: ## 1 ##-- 2 --

// Replace the tags in the text with values from the object
console.log(
  replaceTags("__key1__ __key2__", values, DoubleUnderscores),
);
// Output: ## 1 ## -- 2 --

Changelog

For the latest changes, updates, and improvements to this project, please refer to the Changelog.

License

Copyright (c) 2024 Code Jamboree LLC

This project is licensed under the MIT License - see the LICENSE file for details.

License: MIT

Acknowledgments

Portions of the source code were generated with the assistance of