{"id":16720390,"url":"https://github.com/tomokimiyauci/format","last_synced_at":"2026-03-13T13:32:59.511Z","repository":{"id":172245968,"uuid":"649005983","full_name":"TomokiMiyauci/format","owner":"TomokiMiyauci","description":"Formatting and printing string utilities","archived":false,"fork":false,"pushed_at":"2024-04-11T06:11:08.000Z","size":97,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-16T20:47:28.027Z","etag":null,"topics":["fmt","format","format-string","formatter","interpolate","print","replace","replacement","sprintf","template"],"latest_commit_sha":null,"homepage":"https://deno.land/x/format","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TomokiMiyauci.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-03T13:32:19.000Z","updated_at":"2024-03-17T19:40:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"f8a5deba-0b76-48eb-8923-94d3d046ee92","html_url":"https://github.com/TomokiMiyauci/format","commit_stats":null,"previous_names":["tomokimiyauci/format"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/TomokiMiyauci/format","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomokiMiyauci%2Fformat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomokiMiyauci%2Fformat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomokiMiyauci%2Fformat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomokiMiyauci%2Fformat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TomokiMiyauci","download_url":"https://codeload.github.com/TomokiMiyauci/format/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomokiMiyauci%2Fformat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30467802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T11:00:43.441Z","status":"ssl_error","status_checked_at":"2026-03-13T11:00:23.173Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fmt","format","format-string","formatter","interpolate","print","replace","replacement","sprintf","template"],"created_at":"2024-10-12T22:06:57.670Z","updated_at":"2026-03-13T13:32:59.477Z","avatar_url":"https://github.com/TomokiMiyauci.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# format\n\n[![JSR](https://jsr.io/badges/@miyauci/format)](https://jsr.io/@miyauci/format)\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/TomokiMiyauci/format)](https://github.com/TomokiMiyauci/format/releases)\n[![codecov](https://codecov.io/github/TomokiMiyauci/format/branch/main/graph/badge.svg)](https://codecov.io/gh/TomokiMiyauci/format)\n[![GitHub](https://img.shields.io/github/license/TomokiMiyauci/format)](https://github.com/TomokiMiyauci/format/blob/main/LICENSE)\n\n[![test](https://github.com/TomokiMiyauci/format/actions/workflows/test.yaml/badge.svg)](https://github.com/TomokiMiyauci/format/actions/workflows/test.yaml)\n[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)\n\nFormatting and printing string utilities.\n\n## Table of Contents \u003c!-- omit in toc --\u003e\n\n- [Background](#background)\n- [Install](#install)\n- [Usage](#usage)\n  - [Placeholder](#placeholder)\n  - [Custom serialization](#custom-serialization)\n  - [Override type inference](#override-type-inference)\n  - [No throwing error](#no-throwing-error)\n- [Performance](#performance)\n- [API](#api)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Background\n\nThe purpose of this project is to provide minimum replacement formatting\nsolution.\n\nExisting formatting solutions offer multiple features.\n\nThe Deno community already has\n[std/fmt::sprintf](https://deno.land/std/fmt/printf.ts?doc=\u0026s=sprintf). There\nare also various other 3rd party libraries.\n\nThese could accomplish a lot of work. On the other hand, they are somewhat\nover-specified. You have to pay more cost than you need to. (cost here refers to\ncode size and execution speed).\n\nWe decompose formatting into replacement and serialization. Then, focus on\nreplacement.\n\n## Install\n\ndeno:\n\n```bash\ndeno add @miyauci/format\n```\n\nnpm:\n\n```bash\nnpx jsr add @miyauci/format\n```\n\n## Usage\n\nType inference works well for template literal.\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nassertEquals(format(\"{0} {name}!\", { 0: \"Hello\", name: \"Tom\" }), \"Hello Tom!\");\n\n//@ts-expect-error it should provide params.0 and params.name\nformat(\"{0} {name}!\", {});\n```\n\nIf the specifier is numeric only, you can specify an array as an parameters.\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nassertEquals(format(\"{0} world!\", [\"Hello\"]), \"Hello world!\");\n\n//@ts-expect-error it should provide params.0\nformat(\"{0} world!\", []);\n```\n\n### Placeholder\n\nPlaceholder is a pair of prefix and suffix.\n\n| Name   | Default |\n| ------ | ------- |\n| prefix | `{`     |\n| suffix | `}`     |\n\nThis can be changed.\n\nTemplate literal style:\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nconst result = format(\"should be ${expected}, actual ${actual}\", {\n  expected: \"string\",\n  actual: \"number\",\n}, { placeholders: [{ prefix: \"${\", suffix: \"}\" }] });\nassertEquals(result, \"should be string, actual number\");\n\n//@ts-expect-error it should be error\nformat(\"should be ${expected}, actual ${actual}\", {}, {\n  placeholders: [{ prefix: \"${\", suffix: \"}\" }],\n});\n```\n\nPercent style:\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nconst result = format(\"Hello %s!!!\", { \"\": \"world\" }, {\n  placeholders: [{ prefix: \"%\", suffix: \"s\" }],\n});\nassertEquals(result, \"Hello world!!!\");\n```\n\nMultiple placeholders:\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nconst result = format(\"[0] {description}\", {\n  0: new Date(\"2038/1/19 03:14:08\"),\n  description: \"Time stopped\",\n}, {\n  placeholders: [\n    { prefix: \"{\", suffix: \"}\" },\n    { prefix: \"[\", suffix: \"]\" },\n  ],\n});\nassertEquals(result, \"\u003cDate::toString\u003e Time stopped\");\n```\n\nThe computational complexity of placeholder is O(n) compared to parameters. It\nis recommended to reduce the number of placeholders as much as possible.\n\n### Custom serialization\n\nParameter serialization uses the `String` constructor by default.\n\nTo change this, specify the `stringify` option.\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nconst result = format(\"{0}{1}{2}\", [\"1\", 1, true], {\n  stringify: (param) =\u003e {\n    if (typeof param === \"string\") return `\"${param}\"`;\n\n    return String(param);\n  },\n});\nassertEquals(result, `\"1\"1true`);\n```\n\n### Override type inference\n\nIn certain circumstances, template literal types cannot be provided. In such\ncases, generics can be specified.\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\ndeclare const string: string;\n//@ts-expect-error it should provide params.name and params.title\nformat\u003c\"name\" | \"title\"\u003e(string, {});\n```\n\n### No throwing error\n\n`format` does not throw an error. Even if a parameter is missing.\n\nIf type inference is working, there will never be a missing parameter.\nTherefore, no parameter checking is done at runtime.\n\nThe following is valid.\n\n```ts\nimport { format } from \"@miyauci/format\";\nimport { assertEquals } from \"@std/assert\";\n\nassertEquals(format\u003c\"0\"\u003e(\"{0}{1}\", [\"false\"]), \"false{1}\");\n```\n\nIf you specify [generics](#override-type-inference), you must guarantee the\nparameters.\n\nThis also allows you to escape placeholder.\n\n## Performance\n\nSee [performance](docs/performance.md).\n\n## API\n\nSee [jsr doc](https://jsr.io/@miyauci/format) for all APIs.\n\n## Contributing\n\nSee [contributing](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE) © 2023 Tomoki Miyauchi\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomokimiyauci%2Fformat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomokimiyauci%2Fformat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomokimiyauci%2Fformat/lists"}