{"id":15357141,"url":"https://github.com/chocolateboy/conjoin","last_synced_at":"2026-01-20T02:47:12.812Z","repository":{"id":40285337,"uuid":"268336746","full_name":"chocolateboy/conjoin","owner":"chocolateboy","description":"A fast and flexible joiner for iterables and arrays with a tiny footprint","archived":false,"fork":false,"pushed_at":"2023-01-06T07:32:16.000Z","size":791,"stargazers_count":1,"open_issues_count":15,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-04T12:59:02.387Z","etag":null,"topics":["array","array-join","arrays","curried","iterable","iterables","join","joiner","oxford-comma","serial-comma","zero-dependency"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"artistic-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chocolateboy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-31T18:11:27.000Z","updated_at":"2021-11-02T22:20:17.000Z","dependencies_parsed_at":"2023-02-05T15:31:50.617Z","dependency_job_id":null,"html_url":"https://github.com/chocolateboy/conjoin","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/chocolateboy/conjoin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chocolateboy%2Fconjoin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chocolateboy%2Fconjoin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chocolateboy%2Fconjoin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chocolateboy%2Fconjoin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chocolateboy","download_url":"https://codeload.github.com/chocolateboy/conjoin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chocolateboy%2Fconjoin/sbom","scorecard":{"id":278553,"data":{"date":"2025-08-11","repo":{"name":"github.com/chocolateboy/conjoin","commit":"44d8bdc8ae60b9b5d3f3e858050034ff37d9a680"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":6,"reason":"dependency not pinned by hash detected -- score normalized to 6","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/chocolateboy/conjoin/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/chocolateboy/conjoin/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/chocolateboy/conjoin/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/chocolateboy/conjoin/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:50: update your workflow using https://app.stepsecurity.io/secureworkflow/chocolateboy/conjoin/test.yml/master?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   2 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: Artistic License 2.0: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"41 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-7r28-3m3f-r2pr","Warn: Project is vulnerable to: GHSA-r8j5-h5cx-65gg","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-4r62-v4vq-hr96","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-px4h-xg32-q955","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T15:08:35.560Z","repository_id":40285337,"created_at":"2025-08-17T15:08:35.560Z","updated_at":"2025-08-17T15:08:35.560Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28594958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: 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":["array","array-join","arrays","curried","iterable","iterables","join","joiner","oxford-comma","serial-comma","zero-dependency"],"created_at":"2024-10-01T12:33:26.075Z","updated_at":"2026-01-20T02:47:12.792Z","avatar_url":"https://github.com/chocolateboy.png","language":"JavaScript","readme":"# conjoin\n\n[![Build Status](https://github.com/chocolateboy/conjoin/workflows/test/badge.svg)](https://github.com/chocolateboy/conjoin/actions?query=workflow%3Atest)\n[![NPM Version](https://img.shields.io/npm/v/@chocolatey/conjoin.svg)](https://www.npmjs.org/package/@chocolatey/conjoin)\n\n\u003c!-- TOC --\u003e\n\n- [NAME](#name)\n- [FEATURES](#features)\n- [INSTALLATION](#installation)\n- [SYNOPSIS](#synopsis)\n- [DESCRIPTION](#description)\n  - [Why?](#why)\n- [TYPES](#types)\n- [EXPORTS](#exports)\n  - [conjoin](#conjoin-1)\n  - [conjoiner](#conjoiner)\n- [OPTIONS](#options)\n  - [last](#last)\n  - [map](#map)\n  - [pair](#pair)\n  - [serial](#serial)\n  - [with](#with)\n  - [$map](#dollar-map)\n- [DEVELOPMENT](#development)\n- [COMPATIBILITY](#compatibility)\n- [SEE ALSO](#see-also)\n- [VERSION](#version)\n- [AUTHOR](#author)\n- [COPYRIGHT AND LICENSE](#copyright-and-license)\n\n\u003c!-- TOC END --\u003e\n\n# NAME\n\nconjoin - a fast and flexible joiner for iterables and arrays with a tiny footprint\n\n# FEATURES\n\n- works with ES6+ iterables, plain arrays and array-likes\n- custom default, pair, and last separators\n- currying (generate a function with baked-in options)\n- no dependencies\n- \u0026lt; 350 B minified + gzipped\n- fully typed (TypeScript)\n- CDN builds (UMD) - [jsDelivr][], [unpkg][]\n\n# INSTALLATION\n\n```\n$ npm install @chocolatey/conjoin\n```\n\n# SYNOPSIS\n\n```javascript\nimport { conjoin, conjoiner } from '@chocolatey/conjoin'\n\nconst array = ['foo', 'bar', 'baz', 'quux']\nconst arrayLike = { length: 4, ...array }\nconst iterable = new Set(array)\nconst single = array.slice(0, 1)\nconst pair = array.slice(0, 2)\nconst triple = array.slice(0, 3)\n```\n\n#### works with iterables, arrays, and array-likes\n\n```javascript\nconjoin([])        // \"\"\nconjoin(single)    // \"foo\"\nconjoin(pair)      // \"foo and bar\"\nconjoin(triple)    // \"foo, bar and baz\"\nconjoin(array)     // \"foo, bar, baz and quux\"\nconjoin(iterable)  // \"foo, bar, baz and quux\"\nconjoin(arrayLike) // \"foo, bar, baz and quux\"\n```\n\n#### custom separators\n\n```javascript\nconjoin(array, { with: '; ' })   // \"foo; bar; baz and quux\"\nconjoin(array, { last: ' or ' }) // \"foo, bar, baz or quux\"\nconjoin(pair, { pair: '/' })     // \"foo/bar\"\n```\n\n#### transform values\n\n```javascript\nconst map = (value, index) =\u003e JSON.stringify(value)\nconst $map = JSON.stringify\n\nconjoin(array, { map })  // '\"foo\", \"bar\", \"baz\" and \"quux\"'\nconjoin(array, { $map }) // '\"foo\", \"bar\", \"baz\" and \"quux\"'\n```\n\n#### currying\n\n```javascript\nconst map = it =\u003e it.toUpperCase()\nconst join = conjoiner({ map })\n\njoin(array)                    // \"FOO, BAR, BAZ and QUUX\"\njoin(array, { last: ' AND ' }) // \"FOO, BAR, BAZ AND QUUX\"\n```\n\n#### serial comma\n\n```javascript\nconst join = conjoiner({ serial: ' or ' })\n\njoin(pair)  // \"foo or bar\"\njoin(array) // \"foo, bar, baz, or quux\"\n```\n\n# DESCRIPTION\n\nThis module exports a function which can be used to join array/iterable\nelements to form a string. The default and last separators can be customized,\nas well as the separator to use if there are only two elements. Options can\nbe baked into the function by currying.\n\n## Why?\n\nI often need to glue a list of values together with a default separator and a\ndifferent separator for the last two values, e.g. for error messages:\n\n```javascript\nconst want = ['string', 'symbol', 'function']\nconst message = 'Invalid name: expected string, symbol or function'\n```\n\nSeveral libraries which support this are available on NPM, but most haven't\nbeen updated to work with iterables (they throw an error rather than\nconverting), and many come with hardwired, immutable separators. This library\naims to be at least as fast as the other implementations, and more flexible,\nwhile keeping the package size small.\n\n# TYPES\n\nThe following types are referenced in the descriptions below.\n\n```typescript\ntype Joinable\u003cT\u003e = ArrayLike\u003cT\u003e | Iterable\u003cT\u003e;\n\ntype Options\u003cT\u003e = {\n    last?: string;\n    map?: (value: T, index: number) =\u003e any;\n    pair?: string;\n    serial?: string | boolean;\n    with?: string;\n    $map?: (value: T) =\u003e any;\n};\n```\n\n# EXPORTS\n\n\u003ca name=\"conjoin-1\"\u003e\u003c/a\u003e\n## conjoin\n\n- **Type**: `(values: Joinable\u003cT\u003e, options?: Options\u003cT\u003e) ⇒ string`\n- **Alias**: `join`\n\n```javascript\nimport { join } from '@chocolatey/conjoin'\n\njoin(single)                  // \"foo\"\njoin(pair)                    // \"foo and bar\"\njoin(array, { last: ' or ' }) // \"foo, bar, baz or quux\"\n```\n\nTakes an array-like or iterable and joins its values with the supplied\nseparators.\n\n## conjoiner\n\n- **Type**:\n    - `\u003cU\u003e(options?: Options\u003cU\u003e) ⇒ \u003cT\u003e(values: Joinable\u003cT\u003e, options: Options\u003cT\u003e) ⇒ string`\n    - `\u003cU\u003e(options?: Options\u003cU\u003e) ⇒ (values: Joinable\u003cU\u003e) ⇒ string`\n- **Alias**: `joiner`\n\n```javascript\nimport { joiner } from '@chocolatey/conjoin'\n\nconst join = joiner({ last: ' or ' })\n\njoin(pair)                  // \"foo or bar\"\njoin(triple)                // \"foo, bar or baz\"\njoin(array, { with: '/' })  // \"foo/bar/baz or quux\"\njoin(array, { last: ': ' }) // \"foo, bar, baz: quux\"\n```\n\nReturns a function which takes an array/iterable and joins its values with the\nsupplied separators. Options passed to the generated function override the\noptions passed to the generator.\n\n# OPTIONS\n\nThe [`conjoin`](#conjoin-1) and [`conjoiner`](#conjoiner) functions take the\nfollowing options.\n\n## last\n\n- **Type**: `string`\n- **Default**: `\" and \"`\n\n```javascript\njoin(array)                   // \"foo, bar, baz and quux\"\njoin(array, { last: ' or ' }) // \"foo, bar, baz or quux\"\n```\n\nThe separator to use between the last two values. Used if there are three or\nmore values, or if there are two values and no [`pair`](#pair) separator is\ndefined.\n\n## map\n\n- **Type**: `(value: T, index: number) =\u003e any`\n- **Default**: `undefined`\n\n```javascript\nconst repeat = (it, i) =\u003e String(it).repeat(i + 1)\nconst toInt = it =\u003e parseInt(it)\n\njoin(['a', 'b', 'c', 'd'], { map: repeat })    // \"a, bb, ccc and dddd\"\njoin(['1.', '2.', '3.', '4.'], { map: toInt }) // \"1, 2, 3 and 4\"\n```\n\nAn optional function to transform each joined value. If supplied, the function\nis passed the value and its 0-based index within the array/iterable.\n\nNote that functions like `parseInt` need to be wrapped to ensure the second\nargument passed to the map function (the index) isn't disallowed or\n[misinterpreted][parseInt]. Rather than wrapping these functions manually, they can be\nwrapped automatically via the [`$map`](#dollar-map) option.\n\n## pair\n\n- **Type**: `string`\n- **Default**: value of the [`last`](#last) option\n\n```javascript\nconst join = conjoiner({ pair: ' or ', last: ', or ' })\n\njoin(single) // \"foo\"\njoin(pair)   // \"foo or bar\"\njoin(triple) // \"foo, bar, or baz\"\njoin(array)  // \"foo, bar, baz, or quux\"\n```\n\nThe separator to use when there are exactly two values. If not supplied, it\ndefaults to the value of the [`last`](#last) option.\n\nCan be used in conjunction with `last` to produce lists in the\n\"[Oxford comma](https://en.wikipedia.org/wiki/Serial_comma)\" style.\n\n## serial\n\n- **Type**: `string | boolean`\n- **Default**: `undefined`\n\n```javascript\njoin(pair, { serial: ' or ' })                          // \"foo or bar\"\njoin(array, { serial: ' or ' })                         // \"foo, bar, baz, or quux\"\njoin(['eats', 'shoots', 'leaves'], { serial: ' and ' }) // \"eats, shoots, and leaves\"\n```\n\nThis option provides a way to create an Oxford-comma-style list with a single\noption by taking advantage of the fact that the [`last`](#last) separator in such\nlists is the [`pair`](#pair) separator with a comma prepended. Supplying a `serial`\noption of `\u003cstring\u003e` is equivalent to providing a `pair` option of `\u003cstring\u003e`\nand a `last` option of `\",\"` + `\u003cstring\u003e`, e.g.:\n\n#### before\n\n```javascript\nconst join = joiner({ pair: ' or ', last: ', or ' })\n\njoin(pair)  // \"foo or bar\"\njoin(array) // \"foo, bar, baz, or quux\"\n```\n\n#### after\n\n```javascript\nconst join = joiner({ serial: ' or ' })\n\njoin(pair)  // \"foo or bar\"\njoin(array) // \"foo, bar, baz, or quux\"\n```\n\nAs a convenience, if the value is true, it's assigned the value of the\n[`last`](#last) option, e.g.:\n\n```javascript\nconst array = ['eats', 'shoots', 'leaves']\n\nconjoin(array)                   // \"eats, shoots and leaves\"\nconjoin(array, { serial: true }) // \"eats, shoots, and leaves\"\n```\n\n```javascript\nconst join = joiner({ last: ' or ' })\n\njoin(array)                      // \"eats, shoots or leaves\"\njoin(array, { serial: true })    // \"eats, shoots, or leaves\"\n```\n\n## with\n\n- **Type**: `string`\n- **Default**: `\", \"`\n\n```javascript\njoin(pair,   { with: '/' })     // \"foo and bar\"\njoin(triple, { with: '/' })     // \"foo/bar and baz\"\njoin(triple, { with: ' and ' }) // \"foo and bar and baz\"\n```\n\nThe default separator, used for all but the only ([pair](#pair)) and\n[last](#last) separators.\n\n\u003ca name=\"dollar-map\"\u003e\u003c/a\u003e\n## $map\n\n- **Type**: `(value: T) =\u003e any`\n- **Default**: `undefined`\n\n```javascript\nconst items = ['1.', '2)', '3:', '4/']\n\njoin(items, { $map: parseInt })       // \"1, 2, 3 and 4\"\njoin(array, { $map: JSON.stringify }) // '\"foo\", \"bar\", \"baz\" and \"quux\"'\n```\n\nAn optional function to transform each joined value.\n\nThis is the same as the [`map`](#map) option, but the function is automatically wrapped\nto ensure it's only passed a value rather than a value and its index. This is\nneeded for functions which disallow or [misinterpret][parseInt] additional arguments\nsuch as `parseInt` and `JSON.stringify`.\n\nAssigning a function to `$map` is the same as assigning its wrapper to `map`, so\nthe following are equivalent:\n\n```javascript\nconst map = it =\u003e JSON.stringify(it)\n\njoin(array, { map })                  // '\"foo\", \"bar\", \"baz\" and \"quux\"'\njoin(array, { $map: JSON.stringify }) // '\"foo\", \"bar\", \"baz\" and \"quux\"'\n```\n\nIf both `$map` and `map` are defined, `$map` takes precedence.\n\n# DEVELOPMENT\n\n\u003cdetails\u003e\n\n\u003c!-- TOC:ignore --\u003e\n## NPM Scripts\n\nThe following NPM scripts are available:\n\n- build - compile the library for testing and save to the target directory\n- build:doc - generate the README's TOC (table of contents)\n- build:release - compile the library for release and save to the target directory\n- clean - remove the target directory and its contents\n- rebuild - clean the target directory and recompile the library\n- repl - launch a node REPL with the library loaded\n- test - recompile the library and run the test suite\n- test:run - run the test suite\n- typecheck - sanity check the library's type definitions\n\n\u003c/details\u003e\n\n# COMPATIBILITY\n\n- [Maintained Node.js versions](https://github.com/nodejs/Release#readme) and compatible browsers\n\n# SEE ALSO\n\n- [array-to-sentence](https://www.npmjs.com/package/array-to-sentence)\n- [join-array](https://www.npmjs.com/package/join-array)\n- [joinn](https://www.npmjs.com/package/joinn)\n- [oxford-comma-join](https://www.npmjs.com/package/oxford-comma-join)\n- [oxford-join](https://www.npmjs.com/package/oxford-join)\n\n# VERSION\n\n3.0.1\n\n# AUTHOR\n\n[chocolateboy](https://github.com/chocolateboy)\n\n# COPYRIGHT AND LICENSE\n\nCopyright © 2020 by chocolateboy.\n\nThis is free software; you can redistribute it and/or modify it under the\nterms of the [Artistic License 2.0](https://www.opensource.org/licenses/artistic-license-2.0.php).\n\n[jsDelivr]: https://cdn.jsdelivr.net/npm/@chocolatey/conjoin@3.0.1/dist/index.umd.min.js\n[parseInt]: https://stackoverflow.com/q/262427\n[unpkg]: https://unpkg.com/@chocolatey/conjoin@3.0.1/dist/index.umd.min.js\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchocolateboy%2Fconjoin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchocolateboy%2Fconjoin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchocolateboy%2Fconjoin/lists"}