Full Code of JedWatson/classnames for AI

main dcbb7fb269c0 cached
27 files
40.7 KB
12.6k tokens
37 symbols
1 requests
Download .txt
Repository: JedWatson/classnames
Branch: main
Commit: dcbb7fb269c0
Files: 27
Total size: 40.7 KB

Directory structure:
gitextract_vmzpiaz5/

├── .editorconfig
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── node.js.yml
│       └── release.yml
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── HISTORY.md
├── LICENSE
├── README.md
├── benchmarks/
│   ├── benchmarks.js
│   ├── index.html
│   ├── run.js
│   └── runInBrowser.js
├── bind.d.ts
├── bind.js
├── dedupe.d.ts
├── dedupe.js
├── index.d.ts
├── index.js
├── package.json
└── tests/
    ├── bind.js
    ├── bind.test-d.ts
    ├── dedupe.js
    ├── dedupe.test-d.ts
    ├── index.js
    └── index.test-d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
indent_style = tab

[*.json]
indent_style = space
indent_size = 2

[*.yml]
indent_style = space
indent_size = 2


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily
  - package-ecosystem: npm
    directory: /
    rebase-strategy: disabled
    versioning-strategy: increase
    schedule:
      interval: daily


================================================
FILE: .github/workflows/node.js.yml
================================================
name: Node.js CI

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  test:
    name: Run tests on supported Node.js versions
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        # See supported Node.js versions at https://nodejs.org/en/about/previous-releases
        node-version: [18, 20, 21]

    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

  check-types:
    name: Check type definitions
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Check type definitions
        run: npm run check-types

  benchmarks:
    name: Run benchmarks
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run benchmarks
        run: npm run bench


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  workflow_dispatch:

permissions:
  id-token: write

jobs:
  publish:
    name: Publish package to NPM
    runs-on: ubuntu-latest
    environment: release

    steps:
      - name: Check out repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
          registry-url: https://registry.npmjs.org

      - name: Publish to NPM
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
          NPM_CONFIG_PROVENANCE: true


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory
coverage

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
node_modules

# Users Environment Variables
.lock-wscript

# Mac OS X DS_Store
.DS_Store

benchmarks/runInBrowser.bundle.js


================================================
FILE: .npmignore
================================================
/.*/
/.*
/benchmarks/
/tests/
/CONTRIBUTING.md
/HISTORY.md


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Thanks for your interest in classNames. Issues, PRs and suggestions welcome :)

Before working on a PR, please consider the following:

* Speed is a serious concern for this package as it is likely to be called a
significant number of times in any project that uses it. As such, new features
will only be accepted if they improve (or at least do not negatively impact)
performance.
* To demonstrate performance differences please set up a
[JSPerf](http://jsperf.com) test and link to it from your issue / PR.
* Tests must be added for any change or new feature before it will be accepted.

A benchmark utility is included so that changes may be tested against the
current published version. To run the benchmarks, run `npm install` in the
root directory then run `npm run bench`.

Please be aware though that local benchmarks are just a smoke-signal; they will
run in the v8 version that your local Node.js uses, while classNames is _most_
often run across a wide variety of browsers and browser versions.

It is recommended to test possible regressions in performance in all major
browsers. This can be done by running `npm run bench-browser`, the benchmark
will then be served from http://localhost:8080.


================================================
FILE: HISTORY.md
================================================
# Changelog

## v2.5.1 / 2023-12-29

- Remove `workspaces` field from package ([#350](https://github.com/JedWatson/classnames/pull/350))

## v2.5.0 / 2023-12-27

- Restore ability to pass a TypeScript `interface` ([#341](https://github.com/JedWatson/classnames/pull/341))
- Add `exports` field to package ([#342](https://github.com/JedWatson/classnames/pull/342))

## v2.4.0 / 2023-12-26

- Use string concatenation to increase performance thanks [Jon Koops](https://github.com/jonkoops) ([#336](https://github.com/JedWatson/classnames/pull/336))

## v2.3.3 / 2023-12-21

- Fix default export, thanks [Remco Haszing](https://github.com/remcohaszing) ([#301](https://github.com/JedWatson/classnames/pull/301))
- Fix types for read-only arrays, thanks [Ben Thompson](https://github.com/BenGearset) ([#307](https://github.com/JedWatson/classnames/pull/307))
- Replace README examples with functional-style components, thanks [JoeDGit](https://github.com/JoeDGit) ([#303](https://github.com/JedWatson/classnames/pull/303))

## v2.3.2 / 2022-09-13

- Fix TypeScript types when using require, thanks [Mark Dalgleish](https://github.com/markdalgleish) ([#276](https://github.com/JedWatson/classnames/pull/276))
- Fix toString as `[Object object]` in a vm, thanks [Remco Haszing](https://github.com/remcohaszing) ([#281](https://github.com/JedWatson/classnames/pull/281))

## v2.3.1 / 2021-04-03

- Fix bind/dedupe TypeScript types exports
- Fix mapping Value types, thanks [Remco Haszing](https://github.com/remcohaszing)
- Removed non-existent named exports from types, thanks [Remco Haszing](https://github.com/remcohaszing)

## v2.3.0 / 2021-04-01

- Added TypeScript types
- Added consistent support for custom `.toString()` methods on arguments, thanks [Stanislav Titenko](https://github.com/resetko)

## v2.2.6 / 2018-06-08

- Fixed compatibility issue with usage in an es module environment

## v2.2.5 / 2016-05-02

- Improved performance of `dedupe` variant even further, thanks [Andres Suarez](https://github.com/zertosh)

## v2.2.4 / 2016-04-25

- Improved performance of `dedupe` variant by about 2x, thanks [Bartosz Gościński](https://github.com/bgoscinski)

## v2.2.3 / 2016-01-05

- Updated `bind` variant to use `[].join(' ')` as per the main script in 2.2.2

## v2.2.2 / 2016-01-04

- Switched from string concatenation to `[].join(' ')` for a slight performance gain in the main function.

## v2.2.1 / 2015-11-26

- Add deps parameter to the AMD module, fixes an issue using the Dojo loader, thanks [Chris Jordan](https://github.com/flipperkid)

## v2.2.0 / 2015-10-18

- 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)

## v2.1.5 / 2015-09-30

- reverted a new usage of `Object.keys` in `dedupe.js` that slipped through in the last release

## v2.1.4 / 2015-09-30

- new case added to benchmarks
- safer `hasOwnProperty` check
- AMD module is now named, so you can do the following:

```
define(["classnames"], function (classNames) {
  var style = classNames("foo", "bar");
  // ...
});
```

## v2.1.3 / 2015-07-02

- updated UMD wrapper to support AMD and CommonJS on the same pacge

## v2.1.2 / 2015-05-28

- added a proper UMD wrapper

## v2.1.1 / 2015-05-06

- minor performance improvement thanks to type caching
- improved benchmarking and results output

## v2.1.0 / 2015-05-05

- 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.

## v2.0.0 / 2015-05-03

- 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.

## v1.2.2 / 2015-04-28

- license comment updates to simplify certain build scenarios

## v1.2.1 / 2015-04-22

- added safe exporting for requireJS usage
- clarified Bower usage and instructions

## v1.2.0 / 2015-03-17

- added comprehensive support for array arguments, including nested arrays
- simplified code slightly

## Previous

Please see the git history for the details of previous versions.


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2018 Jed Watson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Classnames

> A simple JavaScript utility for conditionally joining classNames together.

<p>
  <a aria-label="NPM version" href="https://www.npmjs.com/package/classnames">
    <img alt="" src="https://img.shields.io/npm/v/classnames.svg?style=for-the-badge&labelColor=0869B8">
  </a>
  <a aria-label="License" href="#">
    <img alt="" src="https://img.shields.io/npm/l/classnames.svg?style=for-the-badge&labelColor=579805">
  </a>
  <a aria-label="Thinkmill Logo" href="https://www.thinkmill.com.au/open-source?utm_campaign=github-classnames">
    <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">
  </a>

  </p>

Install from the [npm registry](https://www.npmjs.com/) with your package manager:
```bash
npm install classnames
```

Use with [Node.js](https://nodejs.org/en/), [Browserify](https://browserify.org/), or [webpack](https://webpack.github.io/):

```js
const classNames = require('classnames');
classNames('foo', 'bar'); // => 'foo bar'
```

Alternatively, 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.

### Project philosophy

We 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.

Classnames follows the [SemVer](https://semver.org/) standard for versioning.

There is also a [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md).

## Usage

The `classNames` function takes any number of arguments which can be a string or object.
The 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.

```js
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, { baz: null }, ''); // => 'bar'
```

Arrays will be recursively flattened as per the rules above:

```js
const arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
```

### Dynamic class names with ES2015

If 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:

```js
const buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true });
```

### Usage with React.js

This package is the official replacement for `classSet`, which was originally shipped in the React.js Addons bundle.

One 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:

```js
import React, { useState } from 'react';

export default function Button (props) {
  const [isPressed, setIsPressed] = useState(false);
  const [isHovered, setIsHovered] = useState(false);

  let btnClass = 'btn';
  if (isPressed) btnClass += ' btn-pressed';
  else if (isHovered) btnClass += ' btn-over';

  return (
    <button
      className={btnClass}
      onMouseDown={() => setIsPressed(true)}
      onMouseUp={() => setIsPressed(false)}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      {props.label}
    </button>
  );
}
```

You can express the conditional classes more simply as an object:

```js
import React, { useState } from 'react';
import classNames from 'classnames';

export default function Button (props) {
  const [isPressed, setIsPressed] = useState(false);
  const [isHovered, setIsHovered] = useState(false);

  const btnClass = classNames({
    btn: true,
    'btn-pressed': isPressed,
    'btn-over': !isPressed && isHovered,
  });

  return (
    <button
      className={btnClass}
      onMouseDown={() => setIsPressed(true)}
      onMouseUp={() => setIsPressed(false)}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      {props.label}
    </button>
  );
}
```

Because 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:

```js
const btnClass = classNames('btn', this.props.className, {
  'btn-pressed': isPressed,
  'btn-over': !isPressed && isHovered,
});
```

### Alternate `dedupe` version

There 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.

This version is slower (about 5x) so it is offered as an opt-in.

To use the dedupe version with Node.js, Browserify, or webpack:

```js
const classNames = require('classnames/dedupe');

classNames('foo', 'foo', 'bar'); // => 'foo bar'
classNames('foo', { foo: false, bar: true }); // => 'bar'
```

For standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page.

### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules))

If 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.

_Note that in ES2015 environments, it may be better to use the "dynamic class names" approach documented above._

```js
const classNames = require('classnames/bind');

const styles = {
  foo: 'abc',
  bar: 'def',
  baz: 'xyz',
};

const cx = classNames.bind(styles);

const className = cx('foo', ['bar'], { baz: true }); // => 'abc def xyz'
```

Real-world example:

```js
/* components/submit-button.js */
import { useState } from 'react';
import classNames from 'classnames/bind';
import styles from './submit-button.css';

const cx = classNames.bind(styles);

export default function SubmitButton ({ store, form }) {
  const [submissionInProgress, setSubmissionInProgress] = useState(store.submissionInProgress);
  const [errorOccurred, setErrorOccurred] = useState(store.errorOccurred);
  const [valid, setValid] = useState(form.valid);

  const text = submissionInProgress ? 'Processing...' : 'Submit';
  const className = cx({
    base: true,
    inProgress: submissionInProgress,
    error: errorOccurred,
    disabled: valid,
  });

  return <button className={className}>{text}</button>;
}
```

## Polyfills needed to support older browsers

#### `classNames >=2.0.0`

`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.

## LICENSE [MIT](LICENSE)

Copyright (c) 2018 Jed Watson.
Copyright of the Typescript bindings are respective of each contributor listed in the definition file.


================================================
FILE: benchmarks/benchmarks.js
================================================
import { Bench } from 'tinybench';
import { markdownTable } from 'markdown-table';

import local from 'classnames-local';
import bind from 'classnames-local/bind.js';
import dedupe from 'classnames-local/dedupe.js';
import localPackage from 'classnames-local/package.json' with { type: 'json' };

import npm from 'classnames-npm';
import npmDedupe from 'classnames-npm/dedupe.js';
import npmBind from 'classnames-npm/bind.js';
import npmPackage from 'classnames-npm/package.json' with { type: 'json' };

if (localPackage.version !== npmPackage.version) {
	console.warn(
		`Your local version (${localPackage.version}) does not match the installed version (${npmPackage.version}).\n\n` +
		'Please run `npm update classnames-npm` to ensure you are benchmarking against the latest version published to NPM.\n'
	);
}

const benchmarks = [
	{
		description: 'strings',
		args: ['one', 'two', 'three']
	},
	{
		description: 'object',
		args: [{one: true, two: true, three: false}]
	},
	{
		description: 'strings, object',
		args: ['one', 'two', {four: true, three: false}]
	},
	{
		description: 'mix',
		args: ['one', {two: true, three: false}, {four: 'four', five: true}, {}]
	},
	{
		description: 'arrays',
		args: [['one', 'two'], ['three'], ['four', ['five']], [{six: true}, {seven: false}]]
	}
];

export async function runBenchmarks () {
	for (const benchmark of benchmarks) {
		console.log(`Benchmarking '${benchmark.description}'.\n`);
		const bench = await runBenchmark(benchmark);
		printTable(bench);
	}
	
	console.log('Finished!');
}

async function runBenchmark (benchmark) {
	const bench = new Bench();

	bench.add('default/local', () => local(...benchmark.args));
	bench.add('default/npm', () => npm(...benchmark.args));

	bench.add('bind/local', () => bind(...benchmark.args));
	bench.add('bind/npm', () => npmBind(...benchmark.args));

	bench.add('dedupe/local', () => dedupe(...benchmark.args));
	bench.add('dedupe/npm', () => npmDedupe(...benchmark.args));

	await bench.run();
	return bench;
}

function printTable(bench) {
	const table = bench.table();
	const headers = Object.keys(table[0]);
	const data = table.map((entry) => headers.map((header) => entry[header]));

	console.log(markdownTable([headers, ...data]) + '\n');
}


================================================
FILE: benchmarks/index.html
================================================
<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>Benchmarks</title>
	</head>
	<body>
		<button id="start" type="button">Start benchmarks</button>
		<script type="module" src="./runInBrowser.bundle.js"></script>
	</body>
</html>


================================================
FILE: benchmarks/run.js
================================================
import { runBenchmarks } from './benchmarks.js';

await runBenchmarks();


================================================
FILE: benchmarks/runInBrowser.js
================================================
import { runBenchmarks } from './benchmarks.js';

const startButton = document.getElementById('start');

startButton.addEventListener('click', async () => {
	startButton.disabled = true;
	await runBenchmarks();
	startButton.disabled = false;
});


================================================
FILE: bind.d.ts
================================================
import { ArgumentArray } from './index.js';
export type Binding = Record<string, string>;
export default function classNames(this: Binding | undefined, ...args: ArgumentArray): string;


================================================
FILE: bind.js
================================================
const hasOwn = {}.hasOwnProperty;

export default function classNames () {
	let classes = '';

	for (let i = 0; i < arguments.length; i++) {
		const arg = arguments[i];
		if (arg) {
			classes = appendClass(classes, parseValue.call(this, arg));
		}
	}

	return classes;
}

function parseValue (arg) {
	if (typeof arg === 'string') {
		return this && this[arg] || arg;
	}

	if (typeof arg !== 'object') {
		return '';
	}

	if (Array.isArray(arg)) {
		return classNames.apply(this, arg);
	}

	if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
		return arg.toString();
	}

	let classes = '';

	for (const key in arg) {
		if (hasOwn.call(arg, key) && arg[key]) {
			classes = appendClass(classes, this && this[key] || key);
		}
	}

	return classes;
}

function appendClass (value, newClass) {
	if (!newClass) {
		return value;
	}

	return value ? (value + ' ' + newClass) : newClass;
}


================================================
FILE: dedupe.d.ts
================================================
import classNames from './index.js';
export default classNames;


================================================
FILE: dedupe.js
================================================
// Don't inherit from Object so we can skip hasOwnProperty check later.
function StorageObject () {}
StorageObject.prototype = Object.create(null);

export default function classNames () {
	const classSet = new StorageObject();
	appendArray(classSet, arguments);

	let classes = '';

	for (const key in classSet) {
		if (classSet[key]) {
			classes += classes ? (' ' +  key) : key;
		}
	}

	return classes;
}

function appendValue (classSet, arg) {
	if (!arg) return;
	const argType = typeof arg;

	if (argType === 'string') {
		appendString(classSet, arg);
	} else if (Array.isArray(arg)) {
		appendArray(classSet, arg);
	} else if (argType === 'object') {
		appendObject(classSet, arg);
	}
}

const SPACE = /\s+/;

function appendString (classSet, str) {
	const array = str.split(SPACE);
	const length = array.length;

	for (let i = 0; i < length; i++) {
		classSet[array[i]] = true;
	}
}

function appendArray (classSet, array) {
	const length = array.length;

	for (let i = 0; i < length; i++) {
		appendValue(classSet, array[i]);
	}
}

const hasOwn = {}.hasOwnProperty;

function appendObject (classSet, object) {
	if (
		object.toString !== Object.prototype.toString &&
		!object.toString.toString().includes('[native code]')
	) {
		classSet[object.toString()] = true;
		return;
	}

	for (const k in object) {
		if (hasOwn.call(object, k)) {
			// Set value to false instead of deleting it to avoid changing object structure.
			// https://www.smashingmagazine.com/2012/11/writing-fast-memory-efficient-javascript/#de-referencing-misconceptions
			classSet[k] = !!object[k];
		}
	}
}


================================================
FILE: index.d.ts
================================================
// LICENSE is MIT
//
// Copyright (c) 2018
//   Dave Keen <http://www.keendevelopment.ch>
//   Adi Dahiya <https://github.com/adidahiya>
//   Jason Killian <https://github.com/JKillian>
//   Sean Kelley <https://github.com/seansfkelley>
//   Michal Adamczyk <https://github.com/mradamczyk>
//   Marvin Hagemeister <https://github.com/marvinhagemeister>
export type Value = string | boolean | undefined | null;
export type Mapping = Record<string, any>;
export interface ArgumentArray extends Array<Argument> {}
export interface ReadonlyArgumentArray extends ReadonlyArray<Argument> {}
export type Argument = Value | Mapping | ArgumentArray | ReadonlyArgumentArray;
/**
 * A simple JavaScript utility for conditionally joining classNames together.
 */
export default function classNames(...args: ArgumentArray): string;


================================================
FILE: index.js
================================================
const hasOwn = {}.hasOwnProperty;

export default function classNames () {
	let classes = '';

	for (let i = 0; i < arguments.length; i++) {
		const arg = arguments[i];
		if (arg) {
			classes = appendClass(classes, parseValue(arg));
		}
	}

	return classes;
}

function parseValue (arg) {
	if (typeof arg === 'string') {
		return arg;
	}

	if (typeof arg !== 'object') {
		return '';
	}

	if (Array.isArray(arg)) {
		return classNames.apply(null, arg);
	}

	if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
		return arg.toString();
	}

	let classes = '';

	for (const key in arg) {
		if (hasOwn.call(arg, key) && arg[key]) {
			classes = appendClass(classes, key);
		}
	}

	return classes;
}

function appendClass (value, newClass) {
	if (!newClass) {
		return value;
	}

	return value ? (value + ' ' + newClass) : newClass;
}


================================================
FILE: package.json
================================================
{
  "name": "classnames",
  "version": "2.5.1",
  "description": "A simple utility for conditionally joining classNames together",
  "author": "Jed Watson",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/JedWatson/classnames.git"
  },
  "type": "module",
  "main": "./index.js",
  "types": "./index.d.ts",
  "sideEffects": false,
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "types": "./index.d.ts",
      "default": "./index.js"
    },
    "./index.js": {
      "types": "./index.d.ts",
      "default": "./index.js"
    },
    "./bind": {
      "types": "./bind.d.ts",
      "default": "./bind.js"
    },
    "./bind.js": {
      "types": "./bind.d.ts",
      "default": "./bind.js"
    },
    "./dedupe": {
      "types": "./dedupe.d.ts",
      "default": "./dedupe.js"
    },
    "./dedupe.js": {
      "types": "./dedupe.d.ts",
      "default": "./dedupe.js"
    }
  },
  "scripts": {
    "test": "node --test ./tests/*.js",
    "bench": "node ./benchmarks/run.js",
    "bench-browser": "rollup --plugin commonjs,json,node-resolve ./benchmarks/runInBrowser.js --file ./benchmarks/runInBrowser.bundle.js && http-server -c-1 ./benchmarks",
    "check-types": "tsd"
  },
  "keywords": [
    "react",
    "css",
    "classes",
    "classname",
    "classnames",
    "util",
    "utility"
  ],
  "files": [
    "HISTORY.md",
    "LICENSE",
    "README.md",
    "*.d.ts",
    "*.js"
  ],
  "devDependencies": {
    "@rollup/plugin-commonjs": "^29.0.2",
    "@rollup/plugin-json": "^6.1.0",
    "@rollup/plugin-node-resolve": "^16.0.3",
    "classnames-local": "file:.",
    "classnames-npm": "npm:classnames@*",
    "http-server": "^14.1.1",
    "markdown-table": "^3.0.4",
    "rollup": "^4.59.0",
    "tinybench": "^6.0.0",
    "tsd": "^0.31.2"
  },
  "tsd": {
    "directory": "./tests"
  }
}


================================================
FILE: tests/bind.js
================================================
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import classNames from '../bind.js';

const cssModulesMock = {
	a: '#a',
	b: '#b',
	c: '#c',
	d: '#d',
	e: '#e',
	f: '#f'
};

const classNamesBound = classNames.bind(cssModulesMock);

describe('bind', () => {
	describe('classNames', () => {
		it('keeps object keys with truthy values', () => {
			assert.equal(classNames({
				a: true,
				b: false,
				c: 0,
				d: null,
				e: undefined,
				f: 1
			}), 'a f');
		});

		it('joins arrays of class names and ignore falsy values', () => {
			assert.equal(classNames('a', 0, null, undefined, false, 'b'), 'a b');
		});

		it('supports heterogenous arguments', () => {
			assert.equal(classNames({a: true}, 'b', 0), 'a b');
		});

		it('should be trimmed', () => {
			assert.equal(classNames('', 'b', {}, ''), 'b');
		});

		it('returns an empty string for an empty configuration', () => {
			assert.equal(classNames({}), '');
		});

		it('supports an array of class names', () => {
			assert.equal(classNames(['a', 'b']), 'a b');
		});

		it('joins array arguments with string arguments', () => {
			assert.equal(classNames(['a', 'b'], 'c'), 'a b c');
			assert.equal(classNames('c', ['a', 'b']), 'c a b');
		});

		it('handles multiple array arguments', () => {
			assert.equal(classNames(['a', 'b'], ['c', 'd']), 'a b c d');
		});

		it('handles arrays that include falsy and true values', () => {
			assert.equal(classNames(['a', 0, null, undefined, false, true, 'b']), 'a b');
		});

		it('handles arrays that include arrays', () => {
			assert.equal(classNames(['a', ['b', 'c']]), 'a b c');
		});

		it('handles arrays that include objects', () => {
			assert.equal(classNames(['a', {b: true, c: false}]), 'a b');
		});

		it('handles deep array recursion', () => {
			assert.equal(classNames(['a', ['b', ['c', {d: true}]]]), 'a b c d');
		});

		it('handles own toString() method defined on object', () => {
			assert.equal(classNames({
				toString: () => { return 'classFromMethod'; }
			}), 'classFromMethod');
		});
	});

	describe('classNamesBound', () => {
		it('keeps object keys with truthy values', () => {
			assert.equal(classNamesBound({
				a: true,
				b: false,
				c: 0,
				d: null,
				e: undefined,
				f: 1
			}), '#a #f');
		});

		it('keeps class names undefined in bound hash', () => {
			assert.equal(classNamesBound({
				a: true,
				b: false,
				c: 0,
				d: null,
				e: undefined,
				f: 1,
				x: true,
				y: null,
				z: 1
			}), '#a #f x z');
		});

		it('joins arrays of class names and ignore falsy values', () => {
			assert.equal(classNamesBound('a', 0, null, undefined, false, 1, 'b'), '#a #b');
		});

		it('supports heterogenous arguments', () => {
			assert.equal(classNamesBound({a: true}, 'b', 0), '#a #b');
		});

		it('should be trimmed', () => {
			assert.equal(classNamesBound('', 'b', {}, ''), '#b');
		});

		it('returns an empty string for an empty configuration', () => {
			assert.equal(classNamesBound({}), '');
		});

		it('supports an array of class names', () => {
			assert.equal(classNamesBound(['a', 'b']), '#a #b');
		});

		it('joins array arguments with string arguments', () => {
			assert.equal(classNamesBound(['a', 'b'], 'c'), '#a #b #c');
			assert.equal(classNamesBound('c', ['a', 'b']), '#c #a #b');
		});

		it('handles multiple array arguments', () => {
			assert.equal(classNamesBound(['a', 'b'], ['c', 'd']), '#a #b #c #d');
		});

		it('handles arrays that include falsy and true values', () => {
			assert.equal(classNamesBound(['a', 0, null, undefined, false, true, 'b']), '#a #b');
		});

		it('handles arrays that include arrays', () => {
			assert.equal(classNamesBound(['a', ['b', 'c']]), '#a #b #c');
		});

		it('handles arrays that include objects', () => {
			assert.equal(classNamesBound(['a', {b: true, c: false}]), '#a #b');
		});

		it('handles deep array recursion', () => {
			assert.equal(classNamesBound(['a', ['b', ['c', {d: true}]]]), '#a #b #c #d');
		});

		it('handles own toString() method defined on object', () => {
			assert.equal(classNamesBound({
				toString: () => { return 'classFromMethod'; }
			}), 'classFromMethod');
		});

		it('handles toString() method defined inherited in object', () => {
			class Class1 { toString() { return 'classFromMethod'; } }
			class Class2 extends Class1 {}

			assert.equal(classNamesBound(new Class2()), 'classFromMethod');
		});
	});
})


================================================
FILE: tests/bind.test-d.ts
================================================
import {expectError} from 'tsd';
import bind from '../bind';

type Foo = {
  bar: boolean;
};

const foo: Foo = { bar: true };

interface IFoo {
  bar: boolean;
}

const ifoo: IFoo = { bar: true };

// bind
const bound = bind.bind({foo: 'bar'});
bind.bind(undefined);
expectError(bind.bind(Symbol()));
bound('foo');
bound(null);
bound(undefined);
bound(true);
bound(false);
bound({ conditional: true });
bound({ conditional: {} });
bound({ conditional: Symbol() });
bound([]);
bound([['bar', null, undefined, true, false]]);
bound(['bar', null, undefined, true, false]);
bound('bar', null, undefined, true, false);
bound('bar', ['abc', { foo: true }]);
bound('bar', ['abc', { foo: true }], { def: false });
bound('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);
bound(foo);
bound(ifoo);
expectError(bound(Symbol()));
expectError(bound(42));


================================================
FILE: tests/dedupe.js
================================================
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import dedupe from '../dedupe.js';

describe('dedupe', () => {
	it('keeps object keys with truthy values', () => {
		assert.equal(dedupe({
			a: true,
			b: false,
			c: 0,
			d: null,
			e: undefined,
			f: 1
		}), 'a f');
	});

	it('should dedupe', () => {
		assert.equal(dedupe('foo', 'bar', 'foo', 'bar', { foo: true }), 'foo bar');
	});

	it('should make sure subsequent objects can remove/add classes', () => {
		assert.equal(dedupe('foo', { foo: false }, { foo: true, bar: true }), 'foo bar');
	});

	it('should make sure object with falsy value wipe out previous classes', () => {
		assert.equal(dedupe('foo foo', 0, null, undefined, false, 'b', { 'foo': false }), 'b');
		assert.equal(dedupe('foo', 'foobar', 'bar', { foo: false }), 'foobar bar');
		assert.equal(dedupe('foo', 'foo-bar', 'bar', { foo: false }), 'foo-bar bar');
		assert.equal(dedupe('foo', '-moz-foo-bar', 'bar', { foo: false }), '-moz-foo-bar bar');
	});

	it('joins arrays of class names and ignore falsy values', () => {
		assert.equal(dedupe('a', 0, null, undefined, false, 'b'), 'a b');
	});

	it('supports heterogenous arguments', () => {
		assert.equal(dedupe({a: true}, 'b', 0), 'a b');
	});

	it('should be trimmed', () => {
		assert.equal(dedupe('', 'b', {}, ''), 'b');
	});

	it('returns an empty string for an empty configuration', () => {
		assert.equal(dedupe({}), '');
	});

	it('supports an array of class names', () => {
		assert.equal(dedupe(['a', 'b']), 'a b');
	});

	it('joins array arguments with string arguments', () => {
		assert.equal(dedupe(['a', 'b'], 'c'), 'a b c');
		assert.equal(dedupe('c', ['a', 'b']), 'c a b');
	});

	it('handles multiple array arguments', () => {
		assert.equal(dedupe(['a', 'b'], ['c', 'd']), 'a b c d');
	});

	it('handles arrays that include falsy and true values', () => {
		assert.equal(dedupe(['a', 0, null, undefined, false, true, 'b']), 'a b');
	});

	it('handles arrays that include arrays', () => {
		assert.equal(dedupe(['a', ['b', 'c']]), 'a b c');
	});

	it('handles arrays that include objects', () => {
		assert.equal(dedupe(['a', {b: true, c: false}]), 'a b');
	});

	it('handles deep array recursion', () => {
		assert.equal(dedupe(['a', ['b', ['c', {d: true}]]]), 'a b c d');
	});

	it('handles own toString() method defined on object', () => {
		assert.equal(dedupe({
			toString: () => { return 'classFromMethod'; }
		}), 'classFromMethod');
	});

	it('handles toString() method defined inherited in object', () => {
		class Class1 { toString() { return 'classFromMethod'; } }
		class Class2 extends Class1 {}

		assert.equal(dedupe(new Class2()), 'classFromMethod');
	});
});


================================================
FILE: tests/dedupe.test-d.ts
================================================
import {expectError} from 'tsd';
import dedupe from '../dedupe';

type Foo = {
  bar: boolean;
};

const foo: Foo = { bar: true };

interface IFoo {
  bar: boolean;
}

const ifoo: IFoo = { bar: true };

// dedupe
dedupe('foo');
dedupe(null);
dedupe(undefined);
dedupe(true);
dedupe(false);
dedupe({ conditional: true });
dedupe({ conditional: {} });
dedupe({ conditional: Symbol() });
dedupe([]);
dedupe([['bar', null, undefined, true, false]]);
dedupe(['bar', null, undefined, true, false]);
dedupe('bar', null, undefined, true, false);
dedupe('bar', ['abc', { foo: true }]);
dedupe('bar', ['abc', { foo: true }], { def: false });
dedupe('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);
dedupe(foo);
dedupe(ifoo);
expectError(dedupe(Symbol()));
expectError(dedupe(42));


================================================
FILE: tests/index.js
================================================
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import vm from 'node:vm';
import classNames from '../index.js';

describe('classNames', () => {
	it('keeps object keys with truthy values', () => {
		assert.equal(classNames({
			a: true,
			b: false,
			c: 0,
			d: null,
			e: undefined,
			f: 1
		}), 'a f');
	});

	it('joins arrays of class names and ignore falsy values', () => {
		assert.equal(classNames('a', 0, null, undefined, false, 'b'), 'a b');
	});

	it('supports heterogenous arguments', () => {
		assert.equal(classNames({a: true}, 'b', 0), 'a b');
	});

	it('should be trimmed', () => {
		assert.equal(classNames('', 'b', {}, ''), 'b');
	});

	it('returns an empty string for an empty configuration', () => {
		assert.equal(classNames({}), '');
	});

	it('supports an array of class names', () => {
		assert.equal(classNames(['a', 'b']), 'a b');
	});

	it('joins array arguments with string arguments', () => {
		assert.equal(classNames(['a', 'b'], 'c'), 'a b c');
		assert.equal(classNames('c', ['a', 'b']), 'c a b');
	});

	it('handles multiple array arguments', () => {
		assert.equal(classNames(['a', 'b'], ['c', 'd']), 'a b c d');
	});

	it('handles arrays that include falsy and true values', () => {
		assert.equal(classNames(['a', 0, null, undefined, false, true, 'b']), 'a b');
	});

	it('handles arrays that include arrays', () => {
		assert.equal(classNames(['a', ['b', 'c']]), 'a b c');
	});

	it('handles arrays that include objects', () => {
		assert.equal(classNames(['a', {b: true, c: false}]), 'a b');
	});

	it('handles deep array recursion', () => {
		assert.equal(classNames(['a', ['b', ['c', {d: true}]]]), 'a b c d');
	});

	it('handles arrays that are empty', () => {
		assert.equal(classNames('a', []), 'a');
	});

	it('handles nested arrays that have empty nested arrays', () => {
		assert.equal(classNames('a', [[]]), 'a');
	});

	it('handles all types of truthy and falsy property values as expected', () => {
		assert.equal(classNames({
			// falsy:
			null: null,
			emptyString: '',
			noNumber: NaN,
			zero: 0,
			negativeZero: -0,
			false: false,
			undefined: undefined,

			// truthy (literally anything else):
			nonEmptyString: 'foobar',
			whitespace: ' ',
			function: Object.prototype.toString,
			emptyObject: {},
			nonEmptyObject: {a: 1, b: 2},
			emptyList: [],
			nonEmptyList: [1, 2, 3],
			greaterZero: 1
		}), 'nonEmptyString whitespace function emptyObject nonEmptyObject emptyList nonEmptyList greaterZero');
	});

	it('handles toString() method defined on object', () => {
		assert.equal(classNames({
			toString: () => { return 'classFromMethod'; }
		}), 'classFromMethod');
	});

	it('handles toString() method defined inherited in object', () => {
		class Class1 { toString() { return 'classFromMethod'; } }
		class Class2 extends Class1 {}

		assert.equal(classNames(new Class2()), 'classFromMethod');
	});

	it('handles objects in a VM', () => {
		const context = { classNames, output: undefined };
		vm.createContext(context);

		const code = 'output = classNames({ a: true, b: true });';

		vm.runInContext(code, context);
		assert.equal(context.output, 'a b');
	});
});


================================================
FILE: tests/index.test-d.ts
================================================
import {expectError} from 'tsd';
import classNames from '..';

type Foo = {
  bar: boolean;
};

const foo: Foo = { bar: true };

interface IFoo {
  bar: boolean;
}

const ifoo: IFoo = { bar: true };

// default
classNames('foo');
classNames(null);
classNames(undefined);
classNames(true);
classNames(false);
classNames({ conditional: true });
classNames({ conditional: {} });
classNames({ conditional: Symbol() });
classNames([]);
classNames([['bar', null, undefined, true, false]]);
classNames(['bar', null, undefined, true, false]);
classNames('bar', null, undefined, true, false);
classNames('bar', ['abc', { foo: true }]);
classNames('bar', ['abc', { foo: true }], { def: false });
classNames('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }]);
classNames('abc', true, false, undefined, null, { foo: true }, ['abc', true, false, undefined, null, { foo: true }], ['abc', 1234, true, false, undefined, null, { foo: true }] as const);
classNames(foo);
classNames(ifoo);
expectError(classNames(Symbol()));
expectError(classNames(42));
// should match tests/index.js
classNames('c', ['a', 'b']);
classNames('', 'b', {}, '');
classNames('a', null, undefined, true, 'b');
classNames('a', [[]]);
classNames('a', []);
classNames('c', ['a', 'b']);
classNames(['a', 'b']);
classNames(['a', 'b'], 'c');
classNames(['a', 'b'], ['c', 'd']);
classNames(['a', 0, null, undefined, false, true, 'b']);
classNames(['a', ['b', 'c']]);
classNames(['a', ['b', ['c', {d: true}]]]);
classNames(['a', {b: true, c: false}]);
classNames({a: true}, 'b');
classNames({}, [{}, []]);
Download .txt
gitextract_vmzpiaz5/

├── .editorconfig
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── node.js.yml
│       └── release.yml
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── HISTORY.md
├── LICENSE
├── README.md
├── benchmarks/
│   ├── benchmarks.js
│   ├── index.html
│   ├── run.js
│   └── runInBrowser.js
├── bind.d.ts
├── bind.js
├── dedupe.d.ts
├── dedupe.js
├── index.d.ts
├── index.js
├── package.json
└── tests/
    ├── bind.js
    ├── bind.test-d.ts
    ├── dedupe.js
    ├── dedupe.test-d.ts
    ├── index.js
    └── index.test-d.ts
Download .txt
SYMBOL INDEX (37 symbols across 12 files)

FILE: benchmarks/benchmarks.js
  function runBenchmarks (line 44) | async function runBenchmarks () {
  function runBenchmark (line 54) | async function runBenchmark (benchmark) {
  function printTable (line 70) | function printTable(bench) {

FILE: bind.d.ts
  type Binding (line 2) | type Binding = Record<string, string>;

FILE: bind.js
  function classNames (line 3) | function classNames () {
  function parseValue (line 16) | function parseValue (arg) {
  function appendClass (line 44) | function appendClass (value, newClass) {

FILE: dedupe.js
  function StorageObject (line 2) | function StorageObject () {}
  function classNames (line 5) | function classNames () {
  function appendValue (line 20) | function appendValue (classSet, arg) {
  constant SPACE (line 33) | const SPACE = /\s+/;
  function appendString (line 35) | function appendString (classSet, str) {
  function appendArray (line 44) | function appendArray (classSet, array) {
  function appendObject (line 54) | function appendObject (classSet, object) {

FILE: index.d.ts
  type Value (line 10) | type Value = string | boolean | undefined | null;
  type Mapping (line 11) | type Mapping = Record<string, any>;
  type ArgumentArray (line 12) | interface ArgumentArray extends Array<Argument> {}
  type ReadonlyArgumentArray (line 13) | interface ReadonlyArgumentArray extends ReadonlyArray<Argument> {}
  type Argument (line 14) | type Argument = Value | Mapping | ArgumentArray | ReadonlyArgumentArray;

FILE: index.js
  function classNames (line 3) | function classNames () {
  function parseValue (line 16) | function parseValue (arg) {
  function appendClass (line 44) | function appendClass (value, newClass) {

FILE: tests/bind.js
  class Class1 (line 159) | class Class1 { toString() { return 'classFromMethod'; } }
    method toString (line 159) | toString() { return 'classFromMethod'; }
  class Class2 (line 160) | class Class2 extends Class1 {}

FILE: tests/bind.test-d.ts
  type Foo (line 4) | type Foo = {
  type IFoo (line 10) | interface IFoo {

FILE: tests/dedupe.js
  class Class1 (line 84) | class Class1 { toString() { return 'classFromMethod'; } }
    method toString (line 84) | toString() { return 'classFromMethod'; }
  class Class2 (line 85) | class Class2 extends Class1 {}

FILE: tests/dedupe.test-d.ts
  type Foo (line 4) | type Foo = {
  type IFoo (line 10) | interface IFoo {

FILE: tests/index.js
  class Class1 (line 101) | class Class1 { toString() { return 'classFromMethod'; } }
    method toString (line 101) | toString() { return 'classFromMethod'; }
  class Class2 (line 102) | class Class2 extends Class1 {}

FILE: tests/index.test-d.ts
  type Foo (line 4) | type Foo = {
  type IFoo (line 10) | interface IFoo {
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (45K chars).
[
  {
    "path": ".editorconfig",
    "chars": 318,
    "preview": "# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\nroot = true\n\n[*]\nend_of_l"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 255,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n  - packa"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "chars": 1479,
    "preview": "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"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 669,
    "preview": "name: Release\n\non:\n  workflow_dispatch:\n\npermissions:\n  id-token: write\n\njobs:\n  publish:\n    name: Publish package to N"
  },
  {
    "path": ".gitignore",
    "chars": 382,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": ".npmignore",
    "chars": 59,
    "preview": "/.*/\n/.*\n/benchmarks/\n/tests/\n/CONTRIBUTING.md\n/HISTORY.md\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1223,
    "preview": "# Contributing\n\nThanks for your interest in classNames. Issues, PRs and suggestions welcome :)\n\nBefore working on a PR, "
  },
  {
    "path": "HISTORY.md",
    "chars": 4243,
    "preview": "# Changelog\n\n## v2.5.1 / 2023-12-29\n\n- Remove `workspaces` field from package ([#350](https://github.com/JedWatson/class"
  },
  {
    "path": "LICENSE",
    "chars": 1077,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Jed Watson\n\nPermission is hereby granted, free of charge, to any person obtain"
  },
  {
    "path": "README.md",
    "chars": 9137,
    "preview": "# Classnames\n\n> A simple JavaScript utility for conditionally joining classNames together.\n\n<p>\n  <a aria-label=\"NPM ver"
  },
  {
    "path": "benchmarks/benchmarks.js",
    "chars": 2244,
    "preview": "import { Bench } from 'tinybench';\nimport { markdownTable } from 'markdown-table';\n\nimport local from 'classnames-local'"
  },
  {
    "path": "benchmarks/index.html",
    "chars": 254,
    "preview": "<!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"
  },
  {
    "path": "benchmarks/run.js",
    "chars": 73,
    "preview": "import { runBenchmarks } from './benchmarks.js';\n\nawait runBenchmarks();\n"
  },
  {
    "path": "benchmarks/runInBrowser.js",
    "chars": 246,
    "preview": "import { runBenchmarks } from './benchmarks.js';\n\nconst startButton = document.getElementById('start');\n\nstartButton.add"
  },
  {
    "path": "bind.d.ts",
    "chars": 185,
    "preview": "import { ArgumentArray } from './index.js';\nexport type Binding = Record<string, string>;\nexport default function classN"
  },
  {
    "path": "bind.js",
    "chars": 936,
    "preview": "const hasOwn = {}.hasOwnProperty;\n\nexport default function classNames () {\n\tlet classes = '';\n\n\tfor (let i = 0; i < argu"
  },
  {
    "path": "dedupe.d.ts",
    "chars": 64,
    "preview": "import classNames from './index.js';\nexport default classNames;\n"
  },
  {
    "path": "dedupe.js",
    "chars": 1590,
    "preview": "// Don't inherit from Object so we can skip hasOwnProperty check later.\nfunction StorageObject () {}\nStorageObject.proto"
  },
  {
    "path": "index.d.ts",
    "chars": 819,
    "preview": "// LICENSE is MIT\n//\n// Copyright (c) 2018\n//   Dave Keen <http://www.keendevelopment.ch>\n//   Adi Dahiya <https://githu"
  },
  {
    "path": "index.js",
    "chars": 883,
    "preview": "const hasOwn = {}.hasOwnProperty;\n\nexport default function classNames () {\n\tlet classes = '';\n\n\tfor (let i = 0; i < argu"
  },
  {
    "path": "package.json",
    "chars": 1867,
    "preview": "{\n  \"name\": \"classnames\",\n  \"version\": \"2.5.1\",\n  \"description\": \"A simple utility for conditionally joining classNames "
  },
  {
    "path": "tests/bind.js",
    "chars": 4421,
    "preview": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport classNames from '../bind.js';\n"
  },
  {
    "path": "tests/bind.test-d.ts",
    "chars": 899,
    "preview": "import {expectError} from 'tsd';\nimport bind from '../bind';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { bar: t"
  },
  {
    "path": "tests/dedupe.js",
    "chars": 2709,
    "preview": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport dedupe from '../dedupe.js';\n\nd"
  },
  {
    "path": "tests/dedupe.test-d.ts",
    "chars": 829,
    "preview": "import {expectError} from 'tsd';\nimport dedupe from '../dedupe';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { ba"
  },
  {
    "path": "tests/index.js",
    "chars": 3178,
    "preview": "import { describe, it } from 'node:test';\nimport assert from 'node:assert/strict';\nimport vm from 'node:vm';\nimport clas"
  },
  {
    "path": "tests/index.test-d.ts",
    "chars": 1612,
    "preview": "import {expectError} from 'tsd';\nimport classNames from '..';\n\ntype Foo = {\n  bar: boolean;\n};\n\nconst foo: Foo = { bar: "
  }
]

About this extraction

This page contains the full source code of the JedWatson/classnames GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (40.7 KB), approximately 12.6k tokens, and a symbol index with 37 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!