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

dyn-template

v1.0.0

Published

Dynamic Template string

Downloads

9

Readme

dyn-template

English | 中文

Dynamic template strings. It works similarly to JavaScript's template literals, with two key differences:

  • While template literals return a string directly, dynamic templates return a function that returns a string when called
  • If an expression in the dynamic template is a function, that function will be executed each time the dynamic template is called

Installation

npm i dyn-template

Usage

import { dyn } from 'dyn-template'

let n = 0
const random = dyn`${() => ++n}. ${Math.random}`

while (n < 5) console.log(random())

// Possible output:
// 
// 1. 0.7632479109972825
// 2. 0.31000327931995975
// 3. 0.014416508824629193
// 4. 0.7325147663760406
// 5. 0.8938454844327381

If an expression in the dynamic template is a function, it will be executed each time the dynamic template is called.
On the other hand, if an expression is not a function, it will only be evaluated once when creating the dynamic template.

import { dyn } from 'dyn-template'

const template = dyn`Compile time: ${Date.now()}, Runtime: ${Date.now}`

for (let n = 0; n < 1000; ++n) console.log(template()) 

// Possible output:
// 
// Compile time: 1729781340083, Runtime: 1729781340083
// ...
// Compile time: 1729781340083, Runtime: 1729781340084
// ...
// Compile time: 1729781340083, Runtime: 1729781340085
// ...

Advanced Usage

import { dynTemplate, closure } from 'dyn-template'

const fn1 = () => new Date()
const fn2 = Math.random
const template = dynTemplate`str1 ${fn1} str2 ${'non-function expression'} str3 ${fn2} str4`

console.log(template)
// {
//      first: 'str1 ', 
//      fns:  [ [Function: fn1], [Function: fn2] ], 
//      strs: [ ' str2 non-function expression str3 ', ' str4' ] 
// }

const fn = closure(template)    // Convert template to function

console.log(fn())
// Possible output: str1 2024-10-27T15:51:51.000Z str2 non-function expression str3 0.763247910 str4

The object returned by dynTemplate contains three properties:

  • first: The first text block in the template
  • fns: All functions in the template
  • strs: Other text blocks in the template. If there are non-function expressions, they will be evaluated and concatenated into the text blocks

The fns and strs arrays are frozen, so they are immutable, but their elements can be modified.

The closure function converts the above object into a function. Its implementation is as follows:

function closure(template) {
    const { first, fns, strs } = template
    return function () { return fns.reduce((r, p, i) => r + p() + strs[i], first) }
}

So the following two approaches are equivalent:

import { dynTemplate, closure, dyn } from 'dyn-template'

const t1 = closure(dynTemplate`...`)
const t2 = dyn`...`

When you need to further process the template content, you can first use dynTemplate to generate a template object, process it further, and then use closure to convert it into a function.

When you need to modify the closure process, you can first use dynTemplate to generate a template object, then rewrite a function similar to closure:

function compile(template) {
    const { first, fns, strs } = template
    return function (time) {
        // Both fns in the template and the closure fn should accept a number or Date parameter
        const t = time instanceof Date ? time : new Date(time)
        return fns.reduce((r, p, i) => r + p(t) + strs[i], first)   
        // Added a parameter to the call of `p`
    }
}

const template = compile(dynTemplate`Today is ${t => t.getMonth() + 1}/${t => t.getDate()}`)
console.log(template(Date.now())) // Possible output: Today is 10/27

This allows you to customize the closure calling process to implement more complex functionality. clock-reader is implemented using a similar approach.