Skip to content

TomokiMiyauci/format

Repository files navigation

format

JSR GitHub release (latest by date) codecov GitHub

test semantic-release: angular

Formatting and printing string utilities.

Table of Contents

Background

The purpose of this project is to provide minimum replacement formatting solution.

Existing formatting solutions offer multiple features.

The Deno community already has std/fmt::sprintf. There are also various other 3rd party libraries.

These could accomplish a lot of work. On the other hand, they are somewhat over-specified. You have to pay more cost than you need to. (cost here refers to code size and execution speed).

We decompose formatting into replacement and serialization. Then, focus on replacement.

Install

deno:

deno add @miyauci/format

npm:

npx jsr add @miyauci/format

Usage

Type inference works well for template literal.

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

assertEquals(format("{0} {name}!", { 0: "Hello", name: "Tom" }), "Hello Tom!");

//@ts-expect-error it should provide params.0 and params.name
format("{0} {name}!", {});

If the specifier is numeric only, you can specify an array as an parameters.

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

assertEquals(format("{0} world!", ["Hello"]), "Hello world!");

//@ts-expect-error it should provide params.0
format("{0} world!", []);

Placeholder

Placeholder is a pair of prefix and suffix.

Name Default
prefix {
suffix }

This can be changed.

Template literal style:

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

const result = format("should be ${expected}, actual ${actual}", {
  expected: "string",
  actual: "number",
}, { placeholders: [{ prefix: "${", suffix: "}" }] });
assertEquals(result, "should be string, actual number");

//@ts-expect-error it should be error
format("should be ${expected}, actual ${actual}", {}, {
  placeholders: [{ prefix: "${", suffix: "}" }],
});

Percent style:

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

const result = format("Hello %s!!!", { "": "world" }, {
  placeholders: [{ prefix: "%", suffix: "s" }],
});
assertEquals(result, "Hello world!!!");

Multiple placeholders:

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

const result = format("[0] {description}", {
  0: new Date("2038/1/19 03:14:08"),
  description: "Time stopped",
}, {
  placeholders: [
    { prefix: "{", suffix: "}" },
    { prefix: "[", suffix: "]" },
  ],
});
assertEquals(result, "<Date::toString> Time stopped");

The computational complexity of placeholder is O(n) compared to parameters. It is recommended to reduce the number of placeholders as much as possible.

Custom serialization

Parameter serialization uses the String constructor by default.

To change this, specify the stringify option.

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

const result = format("{0}{1}{2}", ["1", 1, true], {
  stringify: (param) => {
    if (typeof param === "string") return `"${param}"`;

    return String(param);
  },
});
assertEquals(result, `"1"1true`);

Override type inference

In certain circumstances, template literal types cannot be provided. In such cases, generics can be specified.

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

declare const string: string;
//@ts-expect-error it should provide params.name and params.title
format<"name" | "title">(string, {});

No throwing error

format does not throw an error. Even if a parameter is missing.

If type inference is working, there will never be a missing parameter. Therefore, no parameter checking is done at runtime.

The following is valid.

import { format } from "@miyauci/format";
import { assertEquals } from "@std/assert";

assertEquals(format<"0">("{0}{1}", ["false"]), "false{1}");

If you specify generics, you must guarantee the parameters.

This also allows you to escape placeholder.

Performance

See performance.

API

See jsr doc for all APIs.

Contributing

See contributing.

License

MIT © 2023 Tomoki Miyauchi