[
  {
    "path": ".editorconfig",
    "content": "# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\nroot = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = false\ninsert_final_newline = true\nindent_style = tab\n\n[*.json]\nindent_style = space\nindent_size = 2\n\n[*.yml]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n  - package-ecosystem: npm\n    directory: /\n    rebase-strategy: disabled\n    versioning-strategy: increase\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "content": "name: Node.js CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  test:\n    name: Run tests on supported Node.js versions\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        # See supported Node.js versions at https://nodejs.org/en/about/previous-releases\n        node-version: [18, 20, 21]\n\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: npm\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run tests\n        run: npm test\n\n  check-types:\n    name: Check type definitions\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: npm\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Check type definitions\n        run: npm run check-types\n\n  benchmarks:\n    name: Run benchmarks\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: npm\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run benchmarks\n        run: npm run bench\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n\npermissions:\n  id-token: write\n\njobs:\n  publish:\n    name: Publish package to NPM\n    runs-on: ubuntu-latest\n    environment: release\n\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: npm\n          registry-url: https://registry.npmjs.org\n\n      - name: Publish to NPM\n        run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n          NPM_CONFIG_PROVENANCE: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory\ncoverage\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\nnode_modules\n\n# Users Environment Variables\n.lock-wscript\n\n# Mac OS X DS_Store\n.DS_Store\n\nbenchmarks/runInBrowser.bundle.js\n"
  },
  {
    "path": ".npmignore",
    "content": "/.*/\n/.*\n/benchmarks/\n/tests/\n/CONTRIBUTING.md\n/HISTORY.md\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nThanks for your interest in classNames. Issues, PRs and suggestions welcome :)\n\nBefore working on a PR, please consider the following:\n\n* Speed is a serious concern for this package as it is likely to be called a\nsignificant number of times in any project that uses it. As such, new features\nwill only be accepted if they improve (or at least do not negatively impact)\nperformance.\n* To demonstrate performance differences please set up a\n[JSPerf](http://jsperf.com) test and link to it from your issue / PR.\n* Tests must be added for any change or new feature before it will be accepted.\n\nA benchmark utility is included so that changes may be tested against the\ncurrent published version. To run the benchmarks, run `npm install` in the\nroot directory then run `npm run bench`.\n\nPlease be aware though that local benchmarks are just a smoke-signal; they will\nrun in the v8 version that your local Node.js uses, while classNames is _most_\noften run across a wide variety of browsers and browser versions.\n\nIt is recommended to test possible regressions in performance in all major\nbrowsers. This can be done by running `npm run bench-browser`, the benchmark\nwill then be served from http://localhost:8080.\n"
  },
  {
    "path": "HISTORY.md",
    "content": "# Changelog\n\n## v2.5.1 / 2023-12-29\n\n- Remove `workspaces` field from package ([#350](https://github.com/JedWatson/classnames/pull/350))\n\n## v2.5.0 / 2023-12-27\n\n- Restore ability to pass a TypeScript `interface` ([#341](https://github.com/JedWatson/classnames/pull/341))\n- Add `exports` field to package ([#342](https://github.com/JedWatson/classnames/pull/342))\n\n## v2.4.0 / 2023-12-26\n\n- Use string concatenation to increase performance thanks [Jon Koops](https://github.com/jonkoops) ([#336](https://github.com/JedWatson/classnames/pull/336))\n\n## v2.3.3 / 2023-12-21\n\n- Fix default export, thanks [Remco Haszing](https://github.com/remcohaszing) ([#301](https://github.com/JedWatson/classnames/pull/301))\n- Fix types for read-only arrays, thanks [Ben Thompson](https://github.com/BenGearset) ([#307](https://github.com/JedWatson/classnames/pull/307))\n- Replace README examples with functional-style components, thanks [JoeDGit](https://github.com/JoeDGit) ([#303](https://github.com/JedWatson/classnames/pull/303))\n\n## v2.3.2 / 2022-09-13\n\n- Fix TypeScript types when using require, thanks [Mark Dalgleish](https://github.com/markdalgleish) ([#276](https://github.com/JedWatson/classnames/pull/276))\n- Fix toString as `[Object object]` in a vm, thanks [Remco Haszing](https://github.com/remcohaszing) ([#281](https://github.com/JedWatson/classnames/pull/281))\n\n## v2.3.1 / 2021-04-03\n\n- Fix bind/dedupe TypeScript types exports\n- Fix mapping Value types, thanks [Remco Haszing](https://github.com/remcohaszing)\n- Removed non-existent named exports from types, thanks [Remco Haszing](https://github.com/remcohaszing)\n\n## v2.3.0 / 2021-04-01\n\n- Added TypeScript types\n- Added consistent support for custom `.toString()` methods on arguments, thanks [Stanislav Titenko](https://github.com/resetko)\n\n## v2.2.6 / 2018-06-08\n\n- Fixed compatibility issue with usage in an es module environment\n\n## v2.2.5 / 2016-05-02\n\n- Improved performance of `dedupe` variant even further, thanks [Andres Suarez](https://github.com/zertosh)\n\n## v2.2.4 / 2016-04-25\n\n- Improved performance of `dedupe` variant by about 2x, thanks [Bartosz Gościński](https://github.com/bgoscinski)\n\n## v2.2.3 / 2016-01-05\n\n- Updated `bind` variant to use `[].join(' ')` as per the main script in 2.2.2\n\n## v2.2.2 / 2016-01-04\n\n- Switched from string concatenation to `[].join(' ')` for a slight performance gain in the main function.\n\n## v2.2.1 / 2015-11-26\n\n- Add deps parameter to the AMD module, fixes an issue using the Dojo loader, thanks [Chris Jordan](https://github.com/flipperkid)\n\n## v2.2.0 / 2015-10-18\n\n- added a new `bind` variant for use with [css-modules](https://github.com/css-modules/css-modules) and similar abstractions, thanks to [Kirill Yakovenko](https://github.com/blia)\n\n## v2.1.5 / 2015-09-30\n\n- reverted a new usage of `Object.keys` in `dedupe.js` that slipped through in the last release\n\n## v2.1.4 / 2015-09-30\n\n- new case added to benchmarks\n- safer `hasOwnProperty` check\n- AMD module is now named, so you can do the following:\n\n```\ndefine([\"classnames\"], function (classNames) {\n  var style = classNames(\"foo\", \"bar\");\n  // ...\n});\n```\n\n## v2.1.3 / 2015-07-02\n\n- updated UMD wrapper to support AMD and CommonJS on the same pacge\n\n## v2.1.2 / 2015-05-28\n\n- added a proper UMD wrapper\n\n## v2.1.1 / 2015-05-06\n\n- minor performance improvement thanks to type caching\n- improved benchmarking and results output\n\n## v2.1.0 / 2015-05-05\n\n- added alternate `dedupe` version of classNames, which is slower (10x) but ensures that if a class is added then overridden by a falsy value in a subsequent argument, it is excluded from the result.\n\n## v2.0.0 / 2015-05-03\n\n- performance improvement; switched to `Array.isArray` for type detection, which is much faster in modern browsers. A polyfill is now required for IE8 support, see the Readme for details.\n\n## v1.2.2 / 2015-04-28\n\n- license comment updates to simplify certain build scenarios\n\n## v1.2.1 / 2015-04-22\n\n- added safe exporting for requireJS usage\n- clarified Bower usage and instructions\n\n## v1.2.0 / 2015-03-17\n\n- added comprehensive support for array arguments, including nested arrays\n- simplified code slightly\n\n## Previous\n\nPlease see the git history for the details of previous versions.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018 Jed Watson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Classnames\n\n> A simple JavaScript utility for conditionally joining classNames together.\n\n<p>\n  <a aria-label=\"NPM version\" href=\"https://www.npmjs.com/package/classnames\">\n    <img alt=\"\" src=\"https://img.shields.io/npm/v/classnames.svg?style=for-the-badge&labelColor=0869B8\">\n  </a>\n  <a aria-label=\"License\" href=\"#\">\n    <img alt=\"\" src=\"https://img.shields.io/npm/l/classnames.svg?style=for-the-badge&labelColor=579805\">\n  </a>\n  <a aria-label=\"Thinkmill Logo\" href=\"https://www.thinkmill.com.au/open-source?utm_campaign=github-classnames\">\n    <img src=\"https://img.shields.io/badge/Sponsored%20BY%20Thinkmill-ed0000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTg2IiBoZWlnaHQ9IjU4NiIgdmlld0JveD0iMCAwIDU4NiA1ODYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xOTk2XzQwNikiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTU4NiAyOTNDNTg2IDQ1NC44MTkgNDU0LjgxOSA1ODYgMjkzIDU4NkMxMzEuMTgxIDU4NiAwIDQ1NC44MTkgMCAyOTNDMCAxMzEuMTgxIDEzMS4xODEgMCAyOTMgMEM0NTQuODE5IDAgNTg2IDEzMS4xODEgNTg2IDI5M1pNMjA1Ljc3NiAzNTguOTQ0QzE5MS4zNzYgMzU4Ljk0NCAxODUuOTA0IDM1Mi4zMiAxODUuOTA0IDMzNS45MDRWMjYyLjc1MkgyMTQuNDE2VjIzNy42OTZIMTg1LjkwNFYyMDEuMTJIMTUzLjA3MlYyMzcuNjk2SDEyOC41OTJWMjYyLjc1MkgxNTMuMDcyVjM0MC44QzE1My4wNzIgMzcyLjc2OCAxNjYuNjA4IDM4NS43MjggMTk3LjQyNCAzODUuNzI4QzIwMy40NzIgMzg1LjcyOCAyMTAuOTYgMzg0LjU3NiAyMTUuODU2IDM4My4xMzZWMzU3LjUwNEMyMTMuNTUyIDM1OC4zNjggMjA5LjUyIDM1OC45NDQgMjA1Ljc3NiAzNTguOTQ0Wk00MDcuMzc2IDIzNC4yNEMzODUuMiAyMzQuMjQgMzcxLjA4OCAyNDQuMDMyIDM2MC40MzIgMjYwLjczNkMzNTIuOTQ0IDI0My40NTYgMzM3LjM5MiAyMzQuMjQgMzE3LjIzMiAyMzQuMjRDMjk5Ljk1MiAyMzQuMjQgMjg2Ljk5MiAyNDEuMTUyIDI3Ni42MjQgMjU1LjI2NEgyNzYuMDQ4VjIzNy42OTZIMjQ0LjY1NlYzODRIMjc3LjQ4OFYzMDUuNjY0QzI3Ny40ODggMjc3LjQ0IDI4OC43MiAyNjAuNzM2IDMwOC4zMDQgMjYwLjczNkMzMjUuMjk2IDI2MC43MzYgMzM0LjUxMiAyNzIuODMyIDMzNC41MTIgMjkzLjU2OFYzODRIMzY3LjM0NFYzMDUuMDg4QzM2Ny4zNDQgMjc3LjE1MiAzNzguODY0IDI2MC43MzYgMzk4LjE2IDI2MC43MzZDNDE0LjU3NiAyNjAuNzM2IDQyNC42NTYgMjcxLjEwNCA0MjQuNjU2IDI5Ny4wMjRWMzg0SDQ1Ny40ODhWMjkzLjg1NkM0NTcuNDg4IDI1NC40IDQzOC40OCAyMzQuMjQgNDA3LjM3NiAyMzQuMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzE5OTZfNDA2Ij4KPHJlY3Qgd2lkdGg9IjU4NiIgaGVpZ2h0PSI1ODYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==&labelColor=C60200&locoColor=white&logoWidth=0\">\n  </a>\n\n  </p>\n\nInstall from the [npm registry](https://www.npmjs.com/) with your package manager:\n```bash\nnpm install classnames\n```\n\nUse with [Node.js](https://nodejs.org/en/), [Browserify](https://browserify.org/), or [webpack](https://webpack.github.io/):\n\n```js\nconst classNames = require('classnames');\nclassNames('foo', 'bar'); // => 'foo bar'\n```\n\nAlternatively, you can simply include `index.js` on your page with a standalone `<script>` tag and it will export a global `classNames` method, or define the module if you are using RequireJS.\n\n### Project philosophy\n\nWe take the stability and performance of this package seriously, because it is run millions of times a day in browsers all around the world. Updates are thoroughly reviewed for performance implications before being released, and we have a comprehensive test suite.\n\nClassnames follows the [SemVer](https://semver.org/) standard for versioning.\n\nThere is also a [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md).\n\n## Usage\n\nThe `classNames` function takes any number of arguments which can be a string or object.\nThe argument `'foo'` is short for `{ foo: true }`. If the value associated with a given key is falsy, that key won't be included in the output.\n\n```js\nclassNames('foo', 'bar'); // => 'foo bar'\nclassNames('foo', { bar: true }); // => 'foo bar'\nclassNames({ 'foo-bar': true }); // => 'foo-bar'\nclassNames({ 'foo-bar': false }); // => ''\nclassNames({ foo: true }, { bar: true }); // => 'foo bar'\nclassNames({ foo: true, bar: true }); // => 'foo bar'\n\n// lots of arguments of various types\nclassNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'\n\n// other falsy values are just ignored\nclassNames(null, false, 'bar', undefined, 0, { baz: null }, ''); // => 'bar'\n```\n\nArrays will be recursively flattened as per the rules above:\n\n```js\nconst arr = ['b', { c: true, d: false }];\nclassNames('a', arr); // => 'a b c'\n```\n\n### Dynamic class names with ES2015\n\nIf you're in an environment that supports [computed keys](https://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) (available in ES2015 and Babel) you can use dynamic class names:\n\n```js\nconst buttonType = 'primary';\nclassNames({ [`btn-${buttonType}`]: true });\n```\n\n### Usage with React.js\n\nThis package is the official replacement for `classSet`, which was originally shipped in the React.js Addons bundle.\n\nOne of its primary use cases is to make dynamic and conditional `className` props simpler to work with (especially more so than conditional string manipulation). So where you may have the following code to generate a `className` prop for a `<button>` in React:\n\n```js\nimport React, { useState } from 'react';\n\nexport default function Button (props) {\n  const [isPressed, setIsPressed] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n\n  let btnClass = 'btn';\n  if (isPressed) btnClass += ' btn-pressed';\n  else if (isHovered) btnClass += ' btn-over';\n\n  return (\n    <button\n      className={btnClass}\n      onMouseDown={() => setIsPressed(true)}\n      onMouseUp={() => setIsPressed(false)}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    >\n      {props.label}\n    </button>\n  );\n}\n```\n\nYou can express the conditional classes more simply as an object:\n\n```js\nimport React, { useState } from 'react';\nimport classNames from 'classnames';\n\nexport default function Button (props) {\n  const [isPressed, setIsPressed] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n\n  const btnClass = classNames({\n    btn: true,\n    'btn-pressed': isPressed,\n    'btn-over': !isPressed && isHovered,\n  });\n\n  return (\n    <button\n      className={btnClass}\n      onMouseDown={() => setIsPressed(true)}\n      onMouseUp={() => setIsPressed(false)}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    >\n      {props.label}\n    </button>\n  );\n}\n```\n\nBecause you can mix together object, array and string arguments, supporting optional `className` props is also simpler as only truthy arguments get included in the result:\n\n```js\nconst btnClass = classNames('btn', this.props.className, {\n  'btn-pressed': isPressed,\n  'btn-over': !isPressed && isHovered,\n});\n```\n\n### Alternate `dedupe` version\n\nThere is an alternate version of `classNames` available which correctly dedupes classes and ensures that falsy classes specified in later arguments are excluded from the result set.\n\nThis version is slower (about 5x) so it is offered as an opt-in.\n\nTo use the dedupe version with Node.js, Browserify, or webpack:\n\n```js\nconst classNames = require('classnames/dedupe');\n\nclassNames('foo', 'foo', 'bar'); // => 'foo bar'\nclassNames('foo', { foo: false, bar: true }); // => 'bar'\n```\n\nFor standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page.\n\n### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules))\n\nIf you are using [css-modules](https://github.com/css-modules/css-modules), or a similar approach to abstract class 'names' and the real `className` values that are actually output to the DOM, you may want to use the `bind` variant.\n\n_Note that in ES2015 environments, it may be better to use the \"dynamic class names\" approach documented above._\n\n```js\nconst classNames = require('classnames/bind');\n\nconst styles = {\n  foo: 'abc',\n  bar: 'def',\n  baz: 'xyz',\n};\n\nconst cx = classNames.bind(styles);\n\nconst className = cx('foo', ['bar'], { baz: true }); // => 'abc def xyz'\n```\n\nReal-world example:\n\n```js\n/* components/submit-button.js */\nimport { useState } from 'react';\nimport classNames from 'classnames/bind';\nimport styles from './submit-button.css';\n\nconst cx = classNames.bind(styles);\n\nexport default function SubmitButton ({ store, form }) {\n  const [submissionInProgress, setSubmissionInProgress] = useState(store.submissionInProgress);\n  const [errorOccurred, setErrorOccurred] = useState(store.errorOccurred);\n  const [valid, setValid] = useState(form.valid);\n\n  const text = submissionInProgress ? 'Processing...' : 'Submit';\n  const className = cx({\n    base: true,\n    inProgress: submissionInProgress,\n    error: errorOccurred,\n    disabled: valid,\n  });\n\n  return <button className={className}>{text}</button>;\n}\n```\n\n## Polyfills needed to support older browsers\n\n#### `classNames >=2.0.0`\n\n`Array.isArray`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill.\n\n## LICENSE [MIT](LICENSE)\n\nCopyright (c) 2018 Jed Watson.\nCopyright of the Typescript bindings are respective of each contributor listed in the definition file.\n"
  },
  {
    "path": "benchmarks/benchmarks.js",
    "content": "import { Bench } from 'tinybench';\nimport { markdownTable } from 'markdown-table';\n\nimport local from 'classnames-local';\nimport bind from 'classnames-local/bind.js';\nimport dedupe from 'classnames-local/dedupe.js';\nimport localPackage from 'classnames-local/package.json' with { type: 'json' };\n\nimport npm from 'classnames-npm';\nimport npmDedupe from 'classnames-npm/dedupe.js';\nimport npmBind from 'classnames-npm/bind.js';\nimport npmPackage from 'classnames-npm/package.json' with { type: 'json' };\n\nif (localPackage.version !== npmPackage.version) {\n\tconsole.warn(\n\t\t`Your local version (${localPackage.version}) does not match the installed version (${npmPackage.version}).\\n\\n` +\n\t\t'Please run `npm update classnames-npm` to ensure you are benchmarking against the latest version published to NPM.\\n'\n\t);\n}\n\nconst benchmarks = [\n\t{\n\t\tdescription: 'strings',\n\t\targs: ['one', 'two', 'three']\n\t},\n\t{\n\t\tdescription: 'object',\n\t\targs: [{one: true, two: true, three: false}]\n\t},\n\t{\n\t\tdescription: 'strings, object',\n\t\targs: ['one', 'two', {four: true, three: false}]\n\t},\n\t{\n\t\tdescription: 'mix',\n\t\targs: ['one', {two: true, three: false}, {four: 'four', five: true}, {}]\n\t},\n\t{\n\t\tdescription: 'arrays',\n\t\targs: [['one', 'two'], ['three'], ['four', ['five']], [{six: true}, {seven: false}]]\n\t}\n];\n\nexport async function runBenchmarks () {\n\tfor (const benchmark of benchmarks) {\n\t\tconsole.log(`Benchmarking '${benchmark.description}'.\\n`);\n\t\tconst bench = await runBenchmark(benchmark);\n\t\tprintTable(bench);\n\t}\n\t\n\tconsole.log('Finished!');\n}\n\nasync function runBenchmark (benchmark) {\n\tconst bench = new Bench();\n\n\tbench.add('default/local', () => local(...benchmark.args));\n\tbench.add('default/npm', () => npm(...benchmark.args));\n\n\tbench.add('bind/local', () => bind(...benchmark.args));\n\tbench.add('bind/npm', () => npmBind(...benchmark.args));\n\n\tbench.add('dedupe/local', () => dedupe(...benchmark.args));\n\tbench.add('dedupe/npm', () => npmDedupe(...benchmark.args));\n\n\tawait bench.run();\n\treturn bench;\n}\n\nfunction printTable(bench) {\n\tconst table = bench.table();\n\tconst headers = Object.keys(table[0]);\n\tconst data = table.map((entry) => headers.map((header) => entry[header]));\n\n\tconsole.log(markdownTable([headers, ...data]) + '\\n');\n}\n"
  },
  {
    "path": "benchmarks/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<title>Benchmarks</title>\n\t</head>\n\t<body>\n\t\t<button id=\"start\" type=\"button\">Start benchmarks</button>\n\t\t<script type=\"module\" src=\"./runInBrowser.bundle.js\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "benchmarks/run.js",
    "content": "import { runBenchmarks } from './benchmarks.js';\n\nawait runBenchmarks();\n"
  },
  {
    "path": "benchmarks/runInBrowser.js",
    "content": "import { runBenchmarks } from './benchmarks.js';\n\nconst startButton = document.getElementById('start');\n\nstartButton.addEventListener('click', async () => {\n\tstartButton.disabled = true;\n\tawait runBenchmarks();\n\tstartButton.disabled = false;\n});\n"
  },
  {
    "path": "bind.d.ts",
    "content": "import { ArgumentArray } from './index.js';\nexport type Binding = Record<string, string>;\nexport default function classNames(this: Binding | undefined, ...args: ArgumentArray): string;\n"
  },
  {
    "path": "bind.js",
    "content": "const hasOwn = {}.hasOwnProperty;\n\nexport default function classNames () {\n\tlet classes = '';\n\n\tfor (let i = 0; i < arguments.length; i++) {\n\t\tconst arg = arguments[i];\n\t\tif (arg) {\n\t\t\tclasses = appendClass(classes, parseValue.call(this, arg));\n\t\t}\n\t}\n\n\treturn classes;\n}\n\nfunction parseValue (arg) {\n\tif (typeof arg === 'string') {\n\t\treturn this && this[arg] || arg;\n\t}\n\n\tif (typeof arg !== 'object') {\n\t\treturn '';\n\t}\n\n\tif (Array.isArray(arg)) {\n\t\treturn classNames.apply(this, arg);\n\t}\n\n\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\treturn arg.toString();\n\t}\n\n\tlet classes = '';\n\n\tfor (const key in arg) {\n\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\tclasses = appendClass(classes, this && this[key] || key);\n\t\t}\n\t}\n\n\treturn classes;\n}\n\nfunction appendClass (value, newClass) {\n\tif (!newClass) {\n\t\treturn value;\n\t}\n\n\treturn value ? (value + ' ' + newClass) : newClass;\n}\n"
  },
  {
    "path": "dedupe.d.ts",
    "content": "import classNames from './index.js';\nexport default classNames;\n"
  },
  {
    "path": "dedupe.js",
    "content": "// Don't inherit from Object so we can skip hasOwnProperty check later.\nfunction StorageObject () {}\nStorageObject.prototype = Object.create(null);\n\nexport default function classNames () {\n\tconst classSet = new StorageObject();\n\tappendArray(classSet, arguments);\n\n\tlet classes = '';\n\n\tfor (const key in classSet) {\n\t\tif (classSet[key]) {\n\t\t\tclasses += classes ? (' ' +  key) : key;\n\t\t}\n\t}\n\n\treturn classes;\n}\n\nfunction appendValue (classSet, arg) {\n\tif (!arg) return;\n\tconst argType = typeof arg;\n\n\tif (argType === 'string') {\n\t\tappendString(classSet, arg);\n\t} else if (Array.isArray(arg)) {\n\t\tappendArray(classSet, arg);\n\t} else if (argType === 'object') {\n\t\tappendObject(classSet, arg);\n\t}\n}\n\nconst SPACE = /\\s+/;\n\nfunction appendString (classSet, str) {\n\tconst array = str.split(SPACE);\n\tconst length = array.length;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tclassSet[array[i]] = true;\n\t}\n}\n\nfunction appendArray (classSet, array) {\n\tconst length = array.length;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tappendValue(classSet, array[i]);\n\t}\n}\n\nconst hasOwn = {}.hasOwnProperty;\n\nfunction appendObject (classSet, object) {\n\tif (\n\t\tobject.toString !== Object.prototype.toString &&\n\t\t!object.toString.toString().includes('[native code]')\n\t) {\n\t\tclassSet[object.toString()] = true;\n\t\treturn;\n\t}\n\n\tfor (const k in object) {\n\t\tif (hasOwn.call(object, k)) {\n\t\t\t// Set value to false instead of deleting it to avoid changing object structure.\n\t\t\t// https://www.smashingmagazine.com/2012/11/writing-fast-memory-efficient-javascript/#de-referencing-misconceptions\n\t\t\tclassSet[k] = !!object[k];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "index.d.ts",
    "content": "// LICENSE is MIT\n//\n// Copyright (c) 2018\n//   Dave Keen <http://www.keendevelopment.ch>\n//   Adi Dahiya <https://github.com/adidahiya>\n//   Jason Killian <https://github.com/JKillian>\n//   Sean Kelley <https://github.com/seansfkelley>\n//   Michal Adamczyk <https://github.com/mradamczyk>\n//   Marvin Hagemeister <https://github.com/marvinhagemeister>\nexport type Value = string | boolean | undefined | null;\nexport type Mapping = Record<string, any>;\nexport interface ArgumentArray extends Array<Argument> {}\nexport interface ReadonlyArgumentArray extends ReadonlyArray<Argument> {}\nexport type Argument = Value | Mapping | ArgumentArray | ReadonlyArgumentArray;\n/**\n * A simple JavaScript utility for conditionally joining classNames together.\n */\nexport default function classNames(...args: ArgumentArray): string;\n"
  },
  {
    "path": "index.js",
    "content": "const hasOwn = {}.hasOwnProperty;\n\nexport default function classNames () {\n\tlet classes = '';\n\n\tfor (let i = 0; i < arguments.length; i++) {\n\t\tconst arg = arguments[i];\n\t\tif (arg) {\n\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t}\n\t}\n\n\treturn classes;\n}\n\nfunction parseValue (arg) {\n\tif (typeof arg === 'string') {\n\t\treturn arg;\n\t}\n\n\tif (typeof arg !== 'object') {\n\t\treturn '';\n\t}\n\n\tif (Array.isArray(arg)) {\n\t\treturn classNames.apply(null, arg);\n\t}\n\n\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\treturn arg.toString();\n\t}\n\n\tlet classes = '';\n\n\tfor (const key in arg) {\n\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\tclasses = appendClass(classes, key);\n\t\t}\n\t}\n\n\treturn classes;\n}\n\nfunction appendClass (value, newClass) {\n\tif (!newClass) {\n\t\treturn value;\n\t}\n\n\treturn value ? (value + ' ' + newClass) : newClass;\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"classnames\",\n  \"version\": \"2.5.1\",\n  \"description\": \"A simple utility for conditionally joining classNames together\",\n  \"author\": \"Jed Watson\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/JedWatson/classnames.git\"\n  },\n  \"type\": \"module\",\n  \"main\": \"./index.js\",\n  \"types\": \"./index.d.ts\",\n  \"sideEffects\": false,\n  \"exports\": {\n    \"./package.json\": \"./package.json\",\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"default\": \"./index.js\"\n    },\n    \"./index.js\": {\n      \"types\": \"./index.d.ts\",\n      \"default\": \"./index.js\"\n    },\n    \"./bind\": {\n      \"types\": \"./bind.d.ts\",\n      \"default\": \"./bind.js\"\n    },\n    \"./bind.js\": {\n      \"types\": \"./bind.d.ts\",\n      \"default\": \"./bind.js\"\n    },\n    \"./dedupe\": {\n      \"types\": \"./dedupe.d.ts\",\n      \"default\": \"./dedupe.js\"\n    },\n    \"./dedupe.js\": {\n      \"types\": \"./dedupe.d.ts\",\n      \"default\": \"./dedupe.js\"\n    }\n  },\n  \"scripts\": {\n    \"test\": \"node --test ./tests/*.js\",\n    \"bench\": \"node ./benchmarks/run.js\",\n    \"bench-browser\": \"rollup --plugin commonjs,json,node-resolve ./benchmarks/runInBrowser.js --file ./benchmarks/runInBrowser.bundle.js && http-server -c-1 ./benchmarks\",\n    \"check-types\": \"tsd\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"css\",\n    \"classes\",\n    \"classname\",\n    \"classnames\",\n    \"util\",\n    \"utility\"\n  ],\n  \"files\": [\n    \"HISTORY.md\",\n    \"LICENSE\",\n    \"README.md\",\n    \"*.d.ts\",\n    \"*.js\"\n  ],\n  \"devDependencies\": {\n    \"@rollup/plugin-commonjs\": \"^29.0.2\",\n    \"@rollup/plugin-json\": \"^6.1.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\n    \"classnames-local\": \"file:.\",\n    \"classnames-npm\": \"npm:classnames@*\",\n    \"http-server\": \"^14.1.1\",\n    \"markdown-table\": \"^3.0.4\",\n    \"rollup\": \"^4.59.0\",\n    \"tinybench\": \"^6.0.0\",\n    \"tsd\": \"^0.31.2\"\n  },\n  \"tsd\": {\n    \"directory\": \"./tests\"\n  }\n}\n"
  },
  {
    "path": "tests/bind.js",
    "content": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport classNames from '../bind.js';\n\nconst cssModulesMock = {\n\ta: '#a',\n\tb: '#b',\n\tc: '#c',\n\td: '#d',\n\te: '#e',\n\tf: '#f'\n};\n\nconst classNamesBound = classNames.bind(cssModulesMock);\n\ndescribe('bind', () => {\n\tdescribe('classNames', () => {\n\t\tit('keeps object keys with truthy values', () => {\n\t\t\tassert.equal(classNames({\n\t\t\t\ta: true,\n\t\t\t\tb: false,\n\t\t\t\tc: 0,\n\t\t\t\td: null,\n\t\t\t\te: undefined,\n\t\t\t\tf: 1\n\t\t\t}), 'a f');\n\t\t});\n\n\t\tit('joins arrays of class names and ignore falsy values', () => {\n\t\t\tassert.equal(classNames('a', 0, null, undefined, false, 'b'), 'a b');\n\t\t});\n\n\t\tit('supports heterogenous arguments', () => {\n\t\t\tassert.equal(classNames({a: true}, 'b', 0), 'a b');\n\t\t});\n\n\t\tit('should be trimmed', () => {\n\t\t\tassert.equal(classNames('', 'b', {}, ''), 'b');\n\t\t});\n\n\t\tit('returns an empty string for an empty configuration', () => {\n\t\t\tassert.equal(classNames({}), '');\n\t\t});\n\n\t\tit('supports an array of class names', () => {\n\t\t\tassert.equal(classNames(['a', 'b']), 'a b');\n\t\t});\n\n\t\tit('joins array arguments with string arguments', () => {\n\t\t\tassert.equal(classNames(['a', 'b'], 'c'), 'a b c');\n\t\t\tassert.equal(classNames('c', ['a', 'b']), 'c a b');\n\t\t});\n\n\t\tit('handles multiple array arguments', () => {\n\t\t\tassert.equal(classNames(['a', 'b'], ['c', 'd']), 'a b c d');\n\t\t});\n\n\t\tit('handles arrays that include falsy and true values', () => {\n\t\t\tassert.equal(classNames(['a', 0, null, undefined, false, true, 'b']), 'a b');\n\t\t});\n\n\t\tit('handles arrays that include arrays', () => {\n\t\t\tassert.equal(classNames(['a', ['b', 'c']]), 'a b c');\n\t\t});\n\n\t\tit('handles arrays that include objects', () => {\n\t\t\tassert.equal(classNames(['a', {b: true, c: false}]), 'a b');\n\t\t});\n\n\t\tit('handles deep array recursion', () => {\n\t\t\tassert.equal(classNames(['a', ['b', ['c', {d: true}]]]), 'a b c d');\n\t\t});\n\n\t\tit('handles own toString() method defined on object', () => {\n\t\t\tassert.equal(classNames({\n\t\t\t\ttoString: () => { return 'classFromMethod'; }\n\t\t\t}), 'classFromMethod');\n\t\t});\n\t});\n\n\tdescribe('classNamesBound', () => {\n\t\tit('keeps object keys with truthy values', () => {\n\t\t\tassert.equal(classNamesBound({\n\t\t\t\ta: true,\n\t\t\t\tb: false,\n\t\t\t\tc: 0,\n\t\t\t\td: null,\n\t\t\t\te: undefined,\n\t\t\t\tf: 1\n\t\t\t}), '#a #f');\n\t\t});\n\n\t\tit('keeps class names undefined in bound hash', () => {\n\t\t\tassert.equal(classNamesBound({\n\t\t\t\ta: true,\n\t\t\t\tb: false,\n\t\t\t\tc: 0,\n\t\t\t\td: null,\n\t\t\t\te: undefined,\n\t\t\t\tf: 1,\n\t\t\t\tx: true,\n\t\t\t\ty: null,\n\t\t\t\tz: 1\n\t\t\t}), '#a #f x z');\n\t\t});\n\n\t\tit('joins arrays of class names and ignore falsy values', () => {\n\t\t\tassert.equal(classNamesBound('a', 0, null, undefined, false, 1, 'b'), '#a #b');\n\t\t});\n\n\t\tit('supports heterogenous arguments', () => {\n\t\t\tassert.equal(classNamesBound({a: true}, 'b', 0), '#a #b');\n\t\t});\n\n\t\tit('should be trimmed', () => {\n\t\t\tassert.equal(classNamesBound('', 'b', {}, ''), '#b');\n\t\t});\n\n\t\tit('returns an empty string for an empty configuration', () => {\n\t\t\tassert.equal(classNamesBound({}), '');\n\t\t});\n\n\t\tit('supports an array of class names', () => {\n\t\t\tassert.equal(classNamesBound(['a', 'b']), '#a #b');\n\t\t});\n\n\t\tit('joins array arguments with string arguments', () => {\n\t\t\tassert.equal(classNamesBound(['a', 'b'], 'c'), '#a #b #c');\n\t\t\tassert.equal(classNamesBound('c', ['a', 'b']), '#c #a #b');\n\t\t});\n\n\t\tit('handles multiple array arguments', () => {\n\t\t\tassert.equal(classNamesBound(['a', 'b'], ['c', 'd']), '#a #b #c #d');\n\t\t});\n\n\t\tit('handles arrays that include falsy and true values', () => {\n\t\t\tassert.equal(classNamesBound(['a', 0, null, undefined, false, true, 'b']), '#a #b');\n\t\t});\n\n\t\tit('handles arrays that include arrays', () => {\n\t\t\tassert.equal(classNamesBound(['a', ['b', 'c']]), '#a #b #c');\n\t\t});\n\n\t\tit('handles arrays that include objects', () => {\n\t\t\tassert.equal(classNamesBound(['a', {b: true, c: false}]), '#a #b');\n\t\t});\n\n\t\tit('handles deep array recursion', () => {\n\t\t\tassert.equal(classNamesBound(['a', ['b', ['c', {d: true}]]]), '#a #b #c #d');\n\t\t});\n\n\t\tit('handles own toString() method defined on object', () => {\n\t\t\tassert.equal(classNamesBound({\n\t\t\t\ttoString: () => { return 'classFromMethod'; }\n\t\t\t}), 'classFromMethod');\n\t\t});\n\n\t\tit('handles toString() method defined inherited in object', () => {\n\t\t\tclass Class1 { toString() { return 'classFromMethod'; } }\n\t\t\tclass Class2 extends Class1 {}\n\n\t\t\tassert.equal(classNamesBound(new Class2()), 'classFromMethod');\n\t\t});\n\t});\n})\n"
  },
  {
    "path": "tests/bind.test-d.ts",
    "content": "import {expectError} from 'tsd';\nimport bind from '../bind';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { bar: true };\n\ninterface IFoo {\n  bar: boolean;\n}\n\nconst ifoo: IFoo = { bar: true };\n\n// bind\nconst bound = bind.bind({foo: 'bar'});\nbind.bind(undefined);\nexpectError(bind.bind(Symbol()));\nbound('foo');\nbound(null);\nbound(undefined);\nbound(true);\nbound(false);\nbound({ conditional: true });\nbound({ conditional: {} });\nbound({ conditional: Symbol() });\nbound([]);\nbound([['bar', null, undefined, true, false]]);\nbound(['bar', null, undefined, true, false]);\nbound('bar', null, undefined, true, false);\nbound('bar', ['abc', { foo: true }]);\nbound('bar', ['abc', { foo: true }], { def: false });\nbound('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);\nbound(foo);\nbound(ifoo);\nexpectError(bound(Symbol()));\nexpectError(bound(42));\n"
  },
  {
    "path": "tests/dedupe.js",
    "content": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport dedupe from '../dedupe.js';\n\ndescribe('dedupe', () => {\n\tit('keeps object keys with truthy values', () => {\n\t\tassert.equal(dedupe({\n\t\t\ta: true,\n\t\t\tb: false,\n\t\t\tc: 0,\n\t\t\td: null,\n\t\t\te: undefined,\n\t\t\tf: 1\n\t\t}), 'a f');\n\t});\n\n\tit('should dedupe', () => {\n\t\tassert.equal(dedupe('foo', 'bar', 'foo', 'bar', { foo: true }), 'foo bar');\n\t});\n\n\tit('should make sure subsequent objects can remove/add classes', () => {\n\t\tassert.equal(dedupe('foo', { foo: false }, { foo: true, bar: true }), 'foo bar');\n\t});\n\n\tit('should make sure object with falsy value wipe out previous classes', () => {\n\t\tassert.equal(dedupe('foo foo', 0, null, undefined, false, 'b', { 'foo': false }), 'b');\n\t\tassert.equal(dedupe('foo', 'foobar', 'bar', { foo: false }), 'foobar bar');\n\t\tassert.equal(dedupe('foo', 'foo-bar', 'bar', { foo: false }), 'foo-bar bar');\n\t\tassert.equal(dedupe('foo', '-moz-foo-bar', 'bar', { foo: false }), '-moz-foo-bar bar');\n\t});\n\n\tit('joins arrays of class names and ignore falsy values', () => {\n\t\tassert.equal(dedupe('a', 0, null, undefined, false, 'b'), 'a b');\n\t});\n\n\tit('supports heterogenous arguments', () => {\n\t\tassert.equal(dedupe({a: true}, 'b', 0), 'a b');\n\t});\n\n\tit('should be trimmed', () => {\n\t\tassert.equal(dedupe('', 'b', {}, ''), 'b');\n\t});\n\n\tit('returns an empty string for an empty configuration', () => {\n\t\tassert.equal(dedupe({}), '');\n\t});\n\n\tit('supports an array of class names', () => {\n\t\tassert.equal(dedupe(['a', 'b']), 'a b');\n\t});\n\n\tit('joins array arguments with string arguments', () => {\n\t\tassert.equal(dedupe(['a', 'b'], 'c'), 'a b c');\n\t\tassert.equal(dedupe('c', ['a', 'b']), 'c a b');\n\t});\n\n\tit('handles multiple array arguments', () => {\n\t\tassert.equal(dedupe(['a', 'b'], ['c', 'd']), 'a b c d');\n\t});\n\n\tit('handles arrays that include falsy and true values', () => {\n\t\tassert.equal(dedupe(['a', 0, null, undefined, false, true, 'b']), 'a b');\n\t});\n\n\tit('handles arrays that include arrays', () => {\n\t\tassert.equal(dedupe(['a', ['b', 'c']]), 'a b c');\n\t});\n\n\tit('handles arrays that include objects', () => {\n\t\tassert.equal(dedupe(['a', {b: true, c: false}]), 'a b');\n\t});\n\n\tit('handles deep array recursion', () => {\n\t\tassert.equal(dedupe(['a', ['b', ['c', {d: true}]]]), 'a b c d');\n\t});\n\n\tit('handles own toString() method defined on object', () => {\n\t\tassert.equal(dedupe({\n\t\t\ttoString: () => { return 'classFromMethod'; }\n\t\t}), 'classFromMethod');\n\t});\n\n\tit('handles toString() method defined inherited in object', () => {\n\t\tclass Class1 { toString() { return 'classFromMethod'; } }\n\t\tclass Class2 extends Class1 {}\n\n\t\tassert.equal(dedupe(new Class2()), 'classFromMethod');\n\t});\n});\n"
  },
  {
    "path": "tests/dedupe.test-d.ts",
    "content": "import {expectError} from 'tsd';\nimport dedupe from '../dedupe';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { bar: true };\n\ninterface IFoo {\n  bar: boolean;\n}\n\nconst ifoo: IFoo = { bar: true };\n\n// dedupe\ndedupe('foo');\ndedupe(null);\ndedupe(undefined);\ndedupe(true);\ndedupe(false);\ndedupe({ conditional: true });\ndedupe({ conditional: {} });\ndedupe({ conditional: Symbol() });\ndedupe([]);\ndedupe([['bar', null, undefined, true, false]]);\ndedupe(['bar', null, undefined, true, false]);\ndedupe('bar', null, undefined, true, false);\ndedupe('bar', ['abc', { foo: true }]);\ndedupe('bar', ['abc', { foo: true }], { def: false });\ndedupe('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);\ndedupe(foo);\ndedupe(ifoo);\nexpectError(dedupe(Symbol()));\nexpectError(dedupe(42));\n"
  },
  {
    "path": "tests/index.js",
    "content": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport vm from 'node:vm';\nimport classNames from '../index.js';\n\ndescribe('classNames', () => {\n\tit('keeps object keys with truthy values', () => {\n\t\tassert.equal(classNames({\n\t\t\ta: true,\n\t\t\tb: false,\n\t\t\tc: 0,\n\t\t\td: null,\n\t\t\te: undefined,\n\t\t\tf: 1\n\t\t}), 'a f');\n\t});\n\n\tit('joins arrays of class names and ignore falsy values', () => {\n\t\tassert.equal(classNames('a', 0, null, undefined, false, 'b'), 'a b');\n\t});\n\n\tit('supports heterogenous arguments', () => {\n\t\tassert.equal(classNames({a: true}, 'b', 0), 'a b');\n\t});\n\n\tit('should be trimmed', () => {\n\t\tassert.equal(classNames('', 'b', {}, ''), 'b');\n\t});\n\n\tit('returns an empty string for an empty configuration', () => {\n\t\tassert.equal(classNames({}), '');\n\t});\n\n\tit('supports an array of class names', () => {\n\t\tassert.equal(classNames(['a', 'b']), 'a b');\n\t});\n\n\tit('joins array arguments with string arguments', () => {\n\t\tassert.equal(classNames(['a', 'b'], 'c'), 'a b c');\n\t\tassert.equal(classNames('c', ['a', 'b']), 'c a b');\n\t});\n\n\tit('handles multiple array arguments', () => {\n\t\tassert.equal(classNames(['a', 'b'], ['c', 'd']), 'a b c d');\n\t});\n\n\tit('handles arrays that include falsy and true values', () => {\n\t\tassert.equal(classNames(['a', 0, null, undefined, false, true, 'b']), 'a b');\n\t});\n\n\tit('handles arrays that include arrays', () => {\n\t\tassert.equal(classNames(['a', ['b', 'c']]), 'a b c');\n\t});\n\n\tit('handles arrays that include objects', () => {\n\t\tassert.equal(classNames(['a', {b: true, c: false}]), 'a b');\n\t});\n\n\tit('handles deep array recursion', () => {\n\t\tassert.equal(classNames(['a', ['b', ['c', {d: true}]]]), 'a b c d');\n\t});\n\n\tit('handles arrays that are empty', () => {\n\t\tassert.equal(classNames('a', []), 'a');\n\t});\n\n\tit('handles nested arrays that have empty nested arrays', () => {\n\t\tassert.equal(classNames('a', [[]]), 'a');\n\t});\n\n\tit('handles all types of truthy and falsy property values as expected', () => {\n\t\tassert.equal(classNames({\n\t\t\t// falsy:\n\t\t\tnull: null,\n\t\t\temptyString: '',\n\t\t\tnoNumber: NaN,\n\t\t\tzero: 0,\n\t\t\tnegativeZero: -0,\n\t\t\tfalse: false,\n\t\t\tundefined: undefined,\n\n\t\t\t// truthy (literally anything else):\n\t\t\tnonEmptyString: 'foobar',\n\t\t\twhitespace: ' ',\n\t\t\tfunction: Object.prototype.toString,\n\t\t\temptyObject: {},\n\t\t\tnonEmptyObject: {a: 1, b: 2},\n\t\t\temptyList: [],\n\t\t\tnonEmptyList: [1, 2, 3],\n\t\t\tgreaterZero: 1\n\t\t}), 'nonEmptyString whitespace function emptyObject nonEmptyObject emptyList nonEmptyList greaterZero');\n\t});\n\n\tit('handles toString() method defined on object', () => {\n\t\tassert.equal(classNames({\n\t\t\ttoString: () => { return 'classFromMethod'; }\n\t\t}), 'classFromMethod');\n\t});\n\n\tit('handles toString() method defined inherited in object', () => {\n\t\tclass Class1 { toString() { return 'classFromMethod'; } }\n\t\tclass Class2 extends Class1 {}\n\n\t\tassert.equal(classNames(new Class2()), 'classFromMethod');\n\t});\n\n\tit('handles objects in a VM', () => {\n\t\tconst context = { classNames, output: undefined };\n\t\tvm.createContext(context);\n\n\t\tconst code = 'output = classNames({ a: true, b: true });';\n\n\t\tvm.runInContext(code, context);\n\t\tassert.equal(context.output, 'a b');\n\t});\n});\n"
  },
  {
    "path": "tests/index.test-d.ts",
    "content": "import {expectError} from 'tsd';\nimport classNames from '..';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { bar: true };\n\ninterface IFoo {\n  bar: boolean;\n}\n\nconst ifoo: IFoo = { bar: true };\n\n// default\nclassNames('foo');\nclassNames(null);\nclassNames(undefined);\nclassNames(true);\nclassNames(false);\nclassNames({ conditional: true });\nclassNames({ conditional: {} });\nclassNames({ conditional: Symbol() });\nclassNames([]);\nclassNames([['bar', null, undefined, true, false]]);\nclassNames(['bar', null, undefined, true, false]);\nclassNames('bar', null, undefined, true, false);\nclassNames('bar', ['abc', { foo: true }]);\nclassNames('bar', ['abc', { foo: true }], { def: false });\nclassNames('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);\nclassNames('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }], ['abc', 1234, true, false, undefined, null, { foo: true }] as const);\nclassNames(foo);\nclassNames(ifoo);\nexpectError(classNames(Symbol()));\nexpectError(classNames(42));\n// should match tests/index.js\nclassNames('c', ['a', 'b']);\nclassNames('', 'b', {}, '');\nclassNames('a', null, undefined, true, 'b');\nclassNames('a', [[]]);\nclassNames('a', []);\nclassNames('c', ['a', 'b']);\nclassNames(['a', 'b']);\nclassNames(['a', 'b'], 'c');\nclassNames(['a', 'b'], ['c', 'd']);\nclassNames(['a', 0, null, undefined, false, true, 'b']);\nclassNames(['a', ['b', 'c']]);\nclassNames(['a', ['b', ['c', {d: true}]]]);\nclassNames(['a', {b: true, c: false}]);\nclassNames({a: true}, 'b');\nclassNames({}, [{}, []]);\n"
  }
]