https://github.com/haltcase/strat
Functional-ish JavaScript string formatting, with inspirations from Python. TypeScript friendly.
https://github.com/haltcase/strat
curried formatting functional partial placeholder python string template typescript utility
Last synced: 11 months ago
JSON representation
Functional-ish JavaScript string formatting, with inspirations from Python. TypeScript friendly.
- Host: GitHub
- URL: https://github.com/haltcase/strat
- Owner: haltcase
- License: mit
- Created: 2016-11-14T18:04:00.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2025-03-14T03:24:19.000Z (11 months ago)
- Last Synced: 2025-03-14T03:33:22.790Z (11 months ago)
- Topics: curried, formatting, functional, partial, placeholder, python, string, template, typescript, utility
- Language: TypeScript
- Homepage:
- Size: 135 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- License: license
Awesome Lists containing this project
README
# strat ·  [](https://www.npmjs.com/package/strat) [](https://www.npmjs.com/package/strat)
_strat_ is a modern, dependency-free TypeScript library for formatting strings
in Deno and Node.js. It's inspired by Python's [`str.format()`][pythonref] but
is focused more on being at home in TypeScript than strictly adhering to
Python's conventions.
> If you want stricter Python conventions, check out
> [string-format][string-format] which served inspired the original version of
> this project.
## features
- formatting can be partially applied, allowing for reusable template functions
- object property references with dot notation
- object methods are called and can be passed arguments
- TypeScript-friendly for Deno and Node.js
## installation
### deno
Import and use:
```ts
import { strat } from "jsr:@haltcase/strat";
import type { FormatPartial, Transformer /*...*/ } from "jsr:@haltcase/strat";
```
### node
1. Install with your method of choice
```shell
pnpm add strat
npm install strat
yarn add strat
bun add strat
```
2. Import
```ts ignore
import { strat } from "strat";
```
## usage
### view from the top
```ts ignore
const series = {
title: "Arcane",
seasons: 2,
rottenTomatoesRating: 100,
};
strat("{title} ({rottenTomatoesRating}%) had {seasons} great seasons", series);
// -> "Arcane (100%) had 2 great seasons."
```
Compare that to the equivalent string concatenation in ES5:
```ts ignore
series.title + " (" + series.rottenTomatoesRating + "%) had " + series.seasons +
" great seasons.";
```
Or the more modern template literals from ES2015:
```ts ignore
`${series.title} (${series.rottenTomatoesRating}%) had ${series.seasons} great seasons.`;
```
But the _strat_ function can also be partially applied to create reusable
template functions. Just leave out the initial replacements like so:
```ts ignore
const template = strat(
"{title} ({rottenTomatoesRating}%) had {seasons} great seasons",
);
template(madMen);
// -> "Mad Men (94%) had 7 great seasons."
template(haltAndCatchFire);
// -> "Halt and Catch Fire (90%) had 4 great seasons."
template(peakyBlinders);
// -> "Peaky Blinders (93%) had 6 great seasons."
```
### partial application
When you omit the `replacements`, `strat` returns a partially applied, reusable
instance.
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const result1 = strat("You got- you gotta run {}.", "Morty");
assertEquals(result1, "You got- you gotta run Morty.");
const result2 = strat("When you come to a {} in the road, {}.", [
"fork",
"take it",
]);
assertEquals(result2, "When you come to a fork in the road, take it.");
```
Here, the first argument is your template string. The second argument is an
array of replacement values, or just a single value.
The second argument can optionally be left out, in which case a new function
will be returned that you can call with your replacement parameters.
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const template = strat("Like {} and {}");
assertEquals(template(["salt", "pepper"]), "Like salt and pepper");
assertEquals(
template(["peanut butter", "jelly"]),
"Like peanut butter and jelly",
);
```
### api
#### `strat(template: string, replacements: any | [...values]): string`
Returns the result of replacing each `{…}` placeholder in the template string
with its corresponding replacement.
Placeholders may contain numbers which refer to positional arguments:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
assertEquals(
strat("{0}, you have {1} unread message{2}", ["Holly", 2, "s"]),
"Holly, you have 2 unread messages",
);
```
Unmatched placeholders produce no output:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
assertEquals(
strat("{0}, you have {1} unread message{2}", ["Steve", 1]),
"Steve, you have 1 unread message",
);
```
A format string may reference a positional argument multiple times:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
assertEquals(
strat(`The name's {1}. {0} {1}.`, ["James", "Bond"]),
"The name's Bond. James Bond.",
);
```
Positional arguments may be referenced implicitly:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
assertEquals(
strat("{}, you have {} unread message{}", ["Steve", 1]),
"Steve, you have 1 unread message",
);
```
A format string must not contain both implicit and explicit references:
```ts ignore
strat("My name is {} {}. Do you like the name {0}?", ["Lemony", "Snicket"]);
// -> Error: cannot mix implicit & explicit formatting
```
Escape `{` and `}` characters by doubling it ( ie. `{{` and `}}` produce `{` and
`}` respectively ):
```ts ignore
strat("{{}} creates an empty {} {}", ["object", "literal"]);
// -> "{} creates an empty object literal"
```
Dot notation may be used to reference object properties:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const rick = { firstName: "Rick", lastName: "Sanchez" };
const morty = { firstName: "Morty", lastName: "Smith" };
assertEquals(
strat("{0.firstName} {0.lastName} and {1.firstName} {1.lastName}", [
rick,
morty,
]),
"Rick Sanchez and Morty Smith",
);
```
`0.` may be omitted when referencing a property of `{0}`:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const song = {
title: "Handlebars",
artist: "Flobots",
album: "Fight With Tools",
};
assertEquals(
strat("{title} | [{artist}] | {album}", song),
"Handlebars | [Flobots] | Fight With Tools",
);
```
If the referenced property is a method, it is invoked with no arguments to
determine the replacement:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const album = {
name: "The Death of Peace of Mind",
artist: "Bad Omens",
releaseDate: new Date("2022-02-25"),
};
assertEquals(
strat("{name} was released {releaseDate.toISOString}.", album),
"The Death of Peace of Mind was released 2022-02-25T00:00:00.000Z.",
);
assertEquals(
strat("Listen to more from {artist.toUpperCase}", album),
"BAD OMENS",
);
```
To pass arguments to a method, pass them as a comma delimited list, with a space
after the method name:
```ts
import { strat } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const person = {
react(tired: string, mood: string) {
if (tired) {
if (mood === "sad") return "cried";
return "rolled his eyes";
} else {
if (mood === "mad") return "broke stuff";
return "shook his fist";
}
},
};
assertEquals(
strat("Average Joe {react true, indifferent}.", person),
"Average Joe rolled his eyes.",
);
```
Note that all arguments are passed as strings, so you'll have to parse them
appropriately if you need, for example, a number or boolean.
However, you can use `_` to pass the falsy `null` value in the argument list:
```ts ignore
strat("Average Joe {react _, mad}.", person);
// -> "Average Joe broke stuff."
```
#### `strat.create(transformers?: { ...functions })`
You can create a new instance of _strat_ by calling `strat.create()`. You may
also optionally supply an object containing transformer functions that you can
use in `strat()` to modify string replacements.
Transformers are very similar to a function you'd pass to
[`Array#map()`][mdn-array-map]. They receive three arguments: the value on which
it's being used, the key, and the full collection of replacements provided to
the template.
```ts ignore
transform(value: string, key: string, collection: [...values]): string
```
To use a transformer, call it by prefixing it with `!` after the field name in
the template string. For example, `{reaction!exclaim}` where `exclaim` was
previously passed in the `transformers` object.
Here's a simple example operating only on the `value` argument:
```ts
import { create } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
const instance = create({
exclaim: (value) => value.toUpperCase() + "!",
});
assertEquals(instance("Hello, {!exclaim}", "world"), "Hello, WORLD!");
```
And here's one that semi-intelligently pluralizes units:
```ts
import { create } from "jsr:@haltcase/strat";
import { assertEquals } from "jsr:@std/assert/equals";
type MiniUnitShape = {
days: number;
daysLabel: string;
};
type NonLabelKeys = Exclude;
const instance = create({
pluralize(_value, key, collection: MiniUnitShape[]) {
const labelSuffix = "Label";
const pluralUnit = key.slice(0, -labelSuffix.length) as NonLabelKeys;
const singularUnit = pluralUnit.slice(0, -1);
return collection[0][pluralUnit] === 1 ? singularUnit : pluralUnit;
},
});
const template = instance("{days} {daysLabel!pluralize}");
assertEquals(template({ days: 2, daysLabel: "days" }), "2 days");
assertEquals(template({ days: 1, daysLabel: "days" }), "1 day");
```
## see also
- [`logger-neue`][logger-neue] – refined logging utility that utilizes
_strat_
## development
_strat_ is built with Deno and cross-published to npm for Node.js.
1. Clone the repo: `git clone https://github.com/haltcase/strat.git`
2. Move into the new directory: `cd strat`
3. Install dependencies: `deno install`
4. Run tests: `deno test`
5. Check code quality (lint, format, types): `deno run qc`
6. Build for npm: `deno run build-npm`
## contributing
Pull requests and any [issues](https://github.com/haltcase/strat/issues) found
are always welcome.
1. Fork the project, and preferably create a branch named something like
`feat-make-better`
2. Follow the development steps [above](#development) but using your forked repo
3. Modify the source files as needed
4. Make sure all tests continue to pass, and it never hurts to have more tests
5. Push & pull request! :tada:
## license
MIT © [Bo Lingen / haltcase](https://github.com/haltcase)
[string-format]: https://github.com/davidchambers/string-format
[pythonref]: http://docs.python.org/library/stdtypes.html#str.format
[mdn-array-map]: https://mdn.io/array/map
[logger-neue]: https://github.com/haltcase/logger-neue