code:
1.15.0 / 2022-03-24
===================

  * deps: send@0.18.0
    - Fix emitted 416 error missing headers property
    - Limit the headers removed for 304 response
    - deps: depd@2.0.0
    - deps: destroy@1.2.0
    - deps: http-errors@2.0.0
    - deps: on-finished@2.4.1
    - deps: statuses@2.0.1

1.14.2 / 2021-12-15
===================

  * deps: send@0.17.2
    - deps: http-errors@1.8.1
    - deps: ms@2.1.3
    - pref: ignore empty http tokens

1.14.1 / 2019-05-10
===================

  * Set stricter CSP header in redirect response
  * deps: send@0.17.1
    - deps: range-parser@~1.2.1

1.14.0 / 2019-05-07
===================

  * deps: parseurl@~1.3.3
  * deps: send@0.17.0
    - deps: http-errors@~1.7.2
    - deps: mime@1.6.0
    - deps: ms@2.1.1
    - deps: statuses@~1.5.0
    - perf: remove redundant `path.normalize` call

1.13.2 / 2018-02-07
===================

  * Fix incorrect end tag in redirects
  * deps: encodeurl@~1.0.2
    - Fix encoding `%` as last character
  * deps: send@0.16.2
    - deps: depd@~1.1.2
    - deps: encodeurl@~1.0.2
    - deps: statuses@~1.4.0

1.13.1 / 2017-09-29
===================

  * Fix regression when `root` is incorrectly set to a file
  * deps: send@0.16.1

1.13.0 / 2017-09-27
===================

  * deps: send@0.16.0
    - Add 70 new types for file extensions
    - Add `immutable` option
    - Fix missing `</html>` in default error & redirects
    - Set charset as "UTF-8" for .js and .json
    - Use instance methods on steam to check for listeners
    - deps: mime@1.4.1
    - perf: improve path validation speed

1.12.6 / 2017-09-22
===================

  * deps: send@0.15.6
    - deps: debug@2.6.9
    - perf: improve `If-Match` token parsing
  * perf: improve slash collapsing

1.12.5 / 2017-09-21
===================

  * deps: parseurl@~1.3.2
    - perf: reduce overhead for full URLs
    - perf: unroll the "fast-path" `RegExp`
  * deps: send@0.15.5
    - Fix handling of modified headers with invalid dates
    - deps: etag@~1.8.1
    - deps: fresh@0.5.2

1.12.4 / 2017-08-05
===================

  * deps: send@0.15.4
    - deps: debug@2.6.8
    - deps: depd@~1.1.1
    - deps: http-errors@~1.6.2

1.12.3 / 2017-05-16
===================

  * deps: send@0.15.3
    - deps: debug@2.6.7

1.12.2 / 2017-04-26
===================

  * deps: send@0.15.2
    - deps: debug@2.6.4

1.12.1 / 2017-03-04
===================

  * deps: send@0.15.1
    - Fix issue when `Date.parse` does not return `NaN` on invalid date
    - Fix strict violation in broken environments

1.12.0 / 2017-02-25
===================

  * Send complete HTML document in redirect response
  * Set default CSP header in redirect response
  * deps: send@0.15.0
    - Fix false detection of `no-cache` request directive
    - Fix incorrect result when `If-None-Match` has both `*` and ETags
    - Fix weak `ETag` matching to match spec
    - Remove usage of `res._headers` private field
    - Support `If-Match` and `If-Unmodified-Since` headers
    - Use `res.getHeaderNames()` when available
    - Use `res.headersSent` when available
    - deps: debug@2.6.1
    - deps: etag@~1.8.0
    - deps: fresh@0.5.0
    - deps: http-errors@~1.6.1

1.11.2 / 2017-01-23
===================

  * deps: send@0.14.2
    - deps: http-errors@~1.5.1
    - deps: ms@0.7.2
    - deps: statuses@~1.3.1

1.11.1 / 2016-06-10
===================

  * Fix redirect error when `req.url` contains raw non-URL characters
  * deps: send@0.14.1

1.11.0 / 2016-06-07
===================

  * Use status code 301 for redirects
  * deps: send@0.14.0
    - Add `acceptRanges` option
    - Add `cacheControl` option
    - Attempt to combine multiple ranges into single range
    - Correctly inherit from `Stream` class
    - Fix `Content-Range` header in 416 responses when using `start`/`end` options
    - Fix `Content-Range` header missing from default 416 responses
    - Ignore non-byte `Range` headers
    - deps: http-errors@~1.5.0
    - deps: range-parser@~1.2.0
    - deps: statuses@~1.3.0
    - perf: remove argument reassignment

1.10.3 / 2016-05-30
===================

  * deps: send@0.13.2
    - Fix invalid `Content-Type` header when `send.mime.default_type` unset

1.10.2 / 2016-01-19
===================

  * deps: parseurl@~1.3.1
    - perf: enable strict mode

1.10.1 / 2016-01-16
===================

  * deps: escape-html@~1.0.3
    - perf: enable strict mode
    - perf: optimize string replacement
    - perf: use faster string coercion
  * deps: send@0.13.1
    - deps: depd@~1.1.0
    - deps: destroy@~1.0.4
    - deps: escape-html@~1.0.3
    - deps: range-parser@~1.0.3

1.10.0 / 2015-06-17
===================

  * Add `fallthrough` option
    - Allows declaring this middleware is the final destination
    - Provides better integration with Express patterns
  * Fix reading options from options prototype
  * Improve the default redirect response headers
  * deps: escape-html@1.0.2
  * deps: send@0.13.0
    - Allow Node.js HTTP server to set `Date` response header
    - Fix incorrectly removing `Content-Location` on 304 response
    - Improve the default redirect response headers
    - Send appropriate headers on default error response
    - Use `http-errors` for standard emitted errors
    - Use `statuses` instead of `http` module for status messages
    - deps: escape-html@1.0.2
    - deps: etag@~1.7.0
    - deps: fresh@0.3.0
    - deps: on-finished@~2.3.0
    - perf: enable strict mode
    - perf: remove unnecessary array allocations
  * perf: enable strict mode
  * perf: remove argument reassignment

1.9.3 / 2015-05-14
==================

  * deps: send@0.12.3
    - deps: debug@~2.2.0
    - deps: depd@~1.0.1
    - deps: etag@~1.6.0
    - deps: ms@0.7.1
    - deps: on-finished@~2.2.1

1.9.2 / 2015-03-14
==================

  * deps: send@0.12.2
    - Throw errors early for invalid `extensions` or `index` options
    - deps: debug@~2.1.3

1.9.1 / 2015-02-17
==================

  * deps: send@0.12.1
    - Fix regression sending zero-length files

1.9.0 / 2015-02-16
==================

  * deps: send@0.12.0
    - Always read the stat size from the file
    - Fix mutating passed-in `options`
    - deps: mime@1.3.4

1.8.1 / 2015-01-20
==================

  * Fix redirect loop in Node.js 0.11.14
  * deps: send@0.11.1
    - Fix root path disclosure

1.8.0 / 2015-01-05
==================

  * deps: send@0.11.0
    - deps: debug@~2.1.1
    - deps: etag@~1.5.1
    - deps: ms@0.7.0
    - deps: on-finished@~2.2.0

1.7.2 / 2015-01-02
==================

  * Fix potential open redirect when mounted at root

1.7.1 / 2014-10-22
==================

  * deps: send@0.10.1
    - deps: on-finished@~2.1.1

1.7.0 / 2014-10-15
==================

  * deps: send@0.10.0
    - deps: debug@~2.1.0
    - deps: depd@~1.0.0
    - deps: etag@~1.5.0

1.6.5 / 2015-02-04
==================

  * Fix potential open redirect when mounted at root
    - Back-ported from v1.7.2

1.6.4 / 2014-10-08
==================

  * Fix redirect loop when index file serving disabled

1.6.3 / 2014-09-24
==================

  * deps: send@0.9.3
    - deps: etag@~1.4.0

1.6.2 / 2014-09-15
==================

  * deps: send@0.9.2
    - deps: depd@0.4.5
    - deps: etag@~1.3.1
    - deps: range-parser@~1.0.2

1.6.1 / 2014-09-07
==================

  * deps: send@0.9.1
    - deps: fresh@0.2.4

1.6.0 / 2014-09-07
==================

  * deps: send@0.9.0
    - Add `lastModified` option
    - Use `etag` to generate `ETag` header
    - deps: debug@~2.0.0

1.5.4 / 2014-09-04
==================

  * deps: send@0.8.5
    - Fix a path traversal issue when using `root`
    - Fix malicious path detection for empty string path

1.5.3 / 2014-08-17
==================

  * deps: send@0.8.3

1.5.2 / 2014-08-14
==================

  * deps: send@0.8.2
    - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream`

1.5.1 / 2014-08-09
==================

  * Fix parsing of weird `req.originalUrl` values
  * deps: parseurl@~1.3.0
  * deps: utils-merge@1.0.0

1.5.0 / 2014-08-05
==================

  * deps: send@0.8.1
    - Add `extensions` option

1.4.4 / 2014-08-04
==================

  * deps: send@0.7.4
    - Fix serving index files without root dir

1.4.3 / 2014-07-29
==================

  * deps: send@0.7.3
    - Fix incorrect 403 on Windows and Node.js 0.11

1.4.2 / 2014-07-27
==================

  * deps: send@0.7.2
    - deps: depd@0.4.4

1.4.1 / 2014-07-26
==================

  * deps: send@0.7.1
    - deps: depd@0.4.3

1.4.0 / 2014-07-21
==================

  * deps: parseurl@~1.2.0
    - Cache URLs based on original value
    - Remove no-longer-needed URL mis-parse work-around
    - Simplify the "fast-path" `RegExp`
  * deps: send@0.7.0
    - Add `dotfiles` option
    - deps: debug@1.0.4
    - deps: depd@0.4.2

1.3.2 / 2014-07-11
==================

  * deps: send@0.6.0
    - Cap `maxAge` value to 1 year
    - deps: debug@1.0.3

1.3.1 / 2014-07-09
==================

  * deps: parseurl@~1.1.3
    - faster parsing of href-only URLs

1.3.0 / 2014-06-28
==================

  * Add `setHeaders` option
  * Include HTML link in redirect response
  * deps: send@0.5.0
    - Accept string for `maxAge` (converted by `ms`)

1.2.3 / 2014-06-11
==================

  * deps: send@0.4.3
    - Do not throw un-catchable error on file open race condition
    - Use `escape-html` for HTML escaping
    - deps: debug@1.0.2
    - deps: finished@1.2.2
    - deps: fresh@0.2.2

1.2.2 / 2014-06-09
==================

  * deps: send@0.4.2
    - fix "event emitter leak" warnings
    - deps: debug@1.0.1
    - deps: finished@1.2.1

1.2.1 / 2014-06-02
==================

  * use `escape-html` for escaping
  * deps: send@0.4.1
    - Send `max-age` in `Cache-Control` in correct format

1.2.0 / 2014-05-29
==================

  * deps: send@0.4.0
    - Calculate ETag with md5 for reduced collisions
    - Fix wrong behavior when index file matches directory
    - Ignore stream errors after request ends
    - Skip directories in index file search
    - deps: debug@0.8.1

1.1.0 / 2014-04-24
==================

  * Accept options directly to `send` module
  * deps: send@0.3.0

1.0.4 / 2014-04-07
==================

  * Resolve relative paths at middleware setup
  * Use parseurl to parse the URL from request

1.0.3 / 2014-03-20
==================

  * Do not rely on connect-like environments

1.0.2 / 2014-03-06
==================

  * deps: send@0.2.0

1.0.1 / 2014-03-05
==================

  * Add mime export for back-compat

1.0.0 / 2014-03-05
==================

  * Genesis from `connect`


code:
# serve-static

[![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][npm-downloads-image]][npm-url]
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]

## Install

This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):

```sh
$ npm install serve-static
```

## API

```js
var serveStatic = require('serve-static')
```

### serveStatic(root, options)

Create a new middleware function to serve files from within a given root
directory. The file to serve will be determined by combining `req.url`
with the provided root directory. When a file is not found, instead of
sending a 404 response, this module will instead call `next()` to move on
to the next middleware, allowing for stacking and fall-backs.

#### Options

##### acceptRanges

Enable or disable accepting ranged requests, defaults to true.
Disabling this will not send `Accept-Ranges` and ignore the contents
of the `Range` request header.

##### cacheControl

Enable or disable setting `Cache-Control` response header, defaults to
true. Disabling this will ignore the `immutable` and `maxAge` options.

##### dotfiles

 Set how "dotfiles" are treated when encountered. A dotfile is a file
or directory that begins with a dot ("."). Note this check is done on
the path itself without checking if the path actually exists on the
disk. If `root` is specified, only the dotfiles above the root are
checked (i.e. the root itself can be within a dotfile when set
to "deny").

  - `'allow'` No special treatment for dotfiles.
  - `'deny'` Deny a request for a dotfile and 403/`next()`.
  - `'ignore'` Pretend like the dotfile does not exist and 404/`next()`.

The default value is similar to `'ignore'`, with the exception that this
default will not ignore the files within a directory that begins with a dot.

##### etag

Enable or disable etag generation, defaults to true.

##### extensions

Set file extension fallbacks. When set, if a file is not found, the given
extensions will be added to the file name and search for. The first that
exists will be served. Example: `['html', 'htm']`.

The default value is `false`.

##### fallthrough

Set the middleware to have client errors fall-through as just unhandled
requests, otherwise forward a client error. The difference is that client
errors like a bad request or a request to a non-existent file will cause
this middleware to simply `next()` to your next middleware when this value
is `true`. When this value is `false`, these errors (even 404s), will invoke
`next(err)`.

Typically `true` is desired such that multiple physical directories can be
mapped to the same web address or for routes to fill in non-existent files.

The value `false` can be used if this middleware is mounted at a path that
is designed to be strictly a single file system directory, which allows for
short-circuiting 404s for less overhead. This middleware will also reply to
all methods.

The default value is `true`.

##### immutable

Enable or disable the `immutable` directive in the `Cache-Control` response
header, defaults to `false`. If set to `true`, the `maxAge` option should
also be specified to enable caching. The `immutable` directive will prevent
supported clients from making conditional requests during the life of the
`maxAge` option to check if the file has changed.

##### index

By default this module will send "index.html" files in response to a request
on a directory. To disable this set `false` or to supply a new index pass a
string or an array in preferred order.

##### lastModified

Enable or disable `Last-Modified` header, defaults to true. Uses the file
system's last modified value.

##### maxAge

Provide a max-age in milliseconds for http caching, defaults to 0. This
can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme)
module.

##### redirect

Redirect to trailing "/" when the pathname is a dir. Defaults to `true`.

##### setHeaders

Function to set custom headers on response. Alterations to the headers need to
occur synchronously. The function is called as `fn(res, path, stat)`, where
the arguments are:

  - `res` the response object
  - `path` the file path that is being sent
  - `stat` the stat object of the file that is being sent

## Examples

### Serve files with vanilla node.js http server

```js
var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')

// Serve up public/ftp folder
var serve = serveStatic('public/ftp', { index: ['index.html', 'index.htm'] })

// Create server
var server = http.createServer(function onRequest (req, res) {
  serve(req, res, finalhandler(req, res))
})

// Listen
server.listen(3000)
```

### Serve all files as downloads

```js
var contentDisposition = require('content-disposition')
var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')

// Serve up public/ftp folder
var serve = serveStatic('public/ftp', {
  index: false,
  setHeaders: setHeaders
})

// Set header to force download
function setHeaders (res, path) {
  res.setHeader('Content-Disposition', contentDisposition(path))
}

// Create server
var server = http.createServer(function onRequest (req, res) {
  serve(req, res, finalhandler(req, res))
})

// Listen
server.listen(3000)
```

### Serving using express

#### Simple

This is a simple example of using Express.

```js
var express = require('express')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic('public/ftp', { index: ['default.html', 'default.htm'] }))
app.listen(3000)
```

#### Multiple roots

This example shows a simple way to search through multiple directories.
Files are searched for in `public-optimized/` first, then `public/` second
as a fallback.

```js
var express = require('express')
var path = require('path')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic(path.join(__dirname, 'public-optimized')))
app.use(serveStatic(path.join(__dirname, 'public')))
app.listen(3000)
```

#### Different settings for paths

This example shows how to set a different max age depending on the served
file type. In this example, HTML files are not cached, while everything else
is for 1 day.

```js
var express = require('express')
var path = require('path')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic(path.join(__dirname, 'public'), {
  maxAge: '1d',
  setHeaders: setCustomCacheControl
}))

app.listen(3000)

function setCustomCacheControl (res, path) {
  if (serveStatic.mime.lookup(path) === 'text/html') {
    // Custom Cache-Control for HTML files
    res.setHeader('Cache-Control', 'public, max-age=0')
  }
}
```

## License

[MIT](LICENSE)

[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/serve-static/master?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-static
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/serve-static/master
[coveralls-url]: https://coveralls.io/r/expressjs/serve-static?branch=master
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/serve-static/master?label=linux
[github-actions-ci-url]: https://github.com/expressjs/serve-static/actions/workflows/ci.yml
[node-image]: https://badgen.net/npm/node/serve-static
[node-url]: https://nodejs.org/en/download/
[npm-downloads-image]: https://badgen.net/npm/dm/serve-static
[npm-url]: https://npmjs.org/package/serve-static
[npm-version-image]: https://badgen.net/npm/v/serve-static

license: mit


code:
/*!
 * serve-static
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * Copyright(c) 2014-2016 Douglas Christopher Wilson
 * MIT Licensed
 */

'use strict'

/**
 * Module dependencies.
 * @private
 */

var encodeUrl = require('encodeurl')
var escapeHtml = require('escape-html')
var parseUrl = require('parseurl')
var resolve = require('path').resolve
var send = require('send')
var url = require('url')

/**
 * Module exports.
 * @public
 */

module.exports = serveStatic
module.exports.mime = send.mime

/**
 * @param {string} root
 * @param {object} [options]
 * @return {function}
 * @public
 */

function serveStatic (root, options) {
  if (!root) {
    throw new TypeError('root path required')
  }

  if (typeof root !== 'string') {
    throw new TypeError('root path must be a string')
  }

  // copy options object
  var opts = Object.create(options || null)

  // fall-though
  var fallthrough = opts.fallthrough !== false

  // default redirect
  var redirect = opts.redirect !== false

  // headers listener
  var setHeaders = opts.setHeaders

  if (setHeaders && typeof setHeaders !== 'function') {
    throw new TypeError('option setHeaders must be function')
  }

  // setup options for send
  opts.maxage = opts.maxage || opts.maxAge || 0
  opts.root = resolve(root)

  // construct directory listener
  var onDirectory = redirect
    ? createRedirectDirectoryListener()
    : createNotFoundDirectoryListener()

  return function serveStatic (req, res, next) {
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      if (fallthrough) {
        return next()
      }

      // method not allowed
      res.statusCode = 405
      res.setHeader('Allow', 'GET, HEAD')
      res.setHeader('Content-Length', '0')
      res.end()
      return
    }

    var forwardError = !fallthrough
    var originalUrl = parseUrl.original(req)
    var path = parseUrl(req).pathname

    // make sure redirect occurs at mount
    if (path === '/' && originalUrl.pathname.substr(-1) !== '/') {
      path = ''
    }

    // create send stream
    var stream = send(req, path, opts)

    // add directory handler
    stream.on('directory', onDirectory)

    // add headers listener
    if (setHeaders) {
      stream.on('headers', setHeaders)
    }

    // add file listener for fallthrough
    if (fallthrough) {
      stream.on('file', function onFile () {
        // once file is determined, always forward error
        forwardError = true
      })
    }

    // forward errors
    stream.on('error', function error (err) {
      if (forwardError || !(err.statusCode < 500)) {
        next(err)
        return
      }

      next()
    })

    // pipe
    stream.pipe(res)
  }
}

/**
 * Collapse all leading slashes into a single slash
 * @private
 */
function collapseLeadingSlashes (str) {
  for (var i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) !== 0x2f /* / */) {
      break
    }
  }

  return i > 1
    ? '/' + str.substr(i)
    : str
}

/**
 * Create a minimal HTML document.
 *
 * @param {string} title
 * @param {string} body
 * @private
 */

function createHtmlDocument (title, body) {
  return '<!DOCTYPE html>\n' +
    '\n' +
    '\n' +
    '<meta charset="utf-8">\n' +
    '<title>' + title + '</title>\n' +
    '\n' +
    '\n' +
    '
' + body + '
\n' +
    '\n' +
    '\n'
}

/**
 * Create a directory listener that just 404s.
 * @private
 */

function createNotFoundDirectoryListener () {
  return function notFound () {
    this.error(404)
  }
}

/**
 * Create a directory listener that performs a redirect.
 * @private
 */

function createRedirectDirectoryListener () {
  return function redirect (res) {
    if (this.hasTrailingSlash()) {
      this.error(404)
      return
    }

    // get original URL
    var originalUrl = parseUrl.original(this.req)

    // append trailing slash
    originalUrl.path = null
    originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')

    // reformat the URL
    var loc = encodeUrl(url.format(originalUrl))
    var doc = createHtmlDocument('Redirecting', 'Redirecting to ' +
      escapeHtml(loc) + '')

    // send redirect response
    res.statusCode = 301
    res.setHeader('Content-Type', 'text/html; charset=UTF-8')
    res.setHeader('Content-Length', Buffer.byteLength(doc))
    res.setHeader('Content-Security-Policy', "default-src 'none'")
    res.setHeader('X-Content-Type-Options', 'nosniff')
    res.setHeader('Location', loc)
    res.end(doc)
  }
}


serve-static

description: Serve static files

author: Douglas Christopher Wilson <doug@somethingdoug.com>

license: MIT

repository: expressjs/serve-static

lint: eslint .

test: mocha --reporter spec --bail --check-leaks test/

test-ci: nyc --reporter=lcov --reporter=text npm test

test-cov: nyc --reporter=html --reporter=text npm test

version: node scripts/version-history.js && git add HISTORY.md


code:
{
	"root": true,

	"extends": "@ljharb",

	"rules": {
		"id-length": "off",
		"new-cap": ["error", {
			"capIsNewExceptions": [
				"GetIntrinsic"
			],
		}],
		"no-extra-parens": "off",
	},

	"overrides": [
		{
			"files": ["test/**/*.js"],
			"rules": {
				"id-length": "off",
				"max-lines-per-function": "off",
				"multiline-comment-style": "off",
				"no-empty-function": "off",
			},
		},
	],
}


code:
# These are supported funding model platforms

github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/set-function-name
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL


reporter: text-summary text html json

exclude: coverage test


code:
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.2.2](https://github.com/ljharb/set-function-length/compare/v1.2.1...v1.2.2) - 2024-03-09

### Commits

- [types] use shared config [`027032f`](https://github.com/ljharb/set-function-length/commit/027032fe9cc439644a07248ea6a8d813fcc767cb)
- [actions] remove redundant finisher; use reusable workflow [`1fd4fb1`](https://github.com/ljharb/set-function-length/commit/1fd4fb1c58bd5170f0dcff7e320077c0aa2ffdeb)
- [types] use a handwritten d.ts file instead of emit [`01b9761`](https://github.com/ljharb/set-function-length/commit/01b9761742c95e1118e8c2d153ce2ae43d9731aa)
- [Deps] update `define-data-property`, `get-intrinsic`, `has-property-descriptors` [`bee8eaf`](https://github.com/ljharb/set-function-length/commit/bee8eaf7749f325357ade85cffeaeef679e513d4)
- [Dev Deps] update `call-bind`, `tape` [`5dae579`](https://github.com/ljharb/set-function-length/commit/5dae579fdc3aab91b14ebb58f9c19ee3f509d434)
- [Tests] use `@arethetypeswrong/cli` [`7e22425`](https://github.com/ljharb/set-function-length/commit/7e22425d15957fd3d6da0b6bca4afc0c8d255d2d)

## [v1.2.1](https://github.com/ljharb/set-function-length/compare/v1.2.0...v1.2.1) - 2024-02-06

### Commits

- [Dev Deps] update `call-bind`, `tape`, `typescript` [`d9a4601`](https://github.com/ljharb/set-function-length/commit/d9a460199c4c1fa37da9ebe055e2c884128f0738)
- [Deps] update `define-data-property`, `get-intrinsic` [`38d39ae`](https://github.com/ljharb/set-function-length/commit/38d39aed13a757ed36211d5b0437b88485090c6b)
- [Refactor] use `es-errors`, so things that only need those do not need `get-intrinsic` [`b4bfe5a`](https://github.com/ljharb/set-function-length/commit/b4bfe5ae0953b906d55b85f867eca5e7f673ebf4)

## [v1.2.0](https://github.com/ljharb/set-function-length/compare/v1.1.1...v1.2.0) - 2024-01-14

### Commits

- [New] add types [`f6d9088`](https://github.com/ljharb/set-function-length/commit/f6d9088b9283a3112b21c6776e8bef6d1f30558a)
- [Fix] ensure `env` properties are always booleans [`0c42f84`](https://github.com/ljharb/set-function-length/commit/0c42f84979086389b3229e1b4272697fd352275a)
- [Dev Deps] update `aud`, `call-bind`, `npmignore`, `tape` [`2b75f75`](https://github.com/ljharb/set-function-length/commit/2b75f75468093a4bb8ce8ca989b2edd2e80d95d1)
- [Deps] update `get-intrinsic`, `has-property-descriptors` [`19bf0fc`](https://github.com/ljharb/set-function-length/commit/19bf0fc4ffaa5ad425acbfa150516be9f3b6263a)
- [meta] add `sideEffects` flag [`8bb9b78`](https://github.com/ljharb/set-function-length/commit/8bb9b78c11c621123f725c9470222f43466c01d0)

## [v1.1.1](https://github.com/ljharb/set-function-length/compare/v1.1.0...v1.1.1) - 2023-10-19

### Fixed

- [Fix] move `define-data-property` to runtime deps [`#2`](https://github.com/ljharb/set-function-length/issues/2)

### Commits

- [Dev Deps] update `object-inspect`; add missing `call-bind` [`5aecf79`](https://github.com/ljharb/set-function-length/commit/5aecf79e7d6400957a5d9bd9ac20d4528908ca18)

## [v1.1.0](https://github.com/ljharb/set-function-length/compare/v1.0.1...v1.1.0) - 2023-10-13

### Commits

- [New] add `env` entry point [`475c87a`](https://github.com/ljharb/set-function-length/commit/475c87aa2f59b700aaed589d980624ec596acdcb)
- [Tests] add coverage with `nyc` [`14f0bf8`](https://github.com/ljharb/set-function-length/commit/14f0bf8c145ae60bf14a026420a06bb7be132c36)
- [eslint] fix linting failure [`fb516f9`](https://github.com/ljharb/set-function-length/commit/fb516f93c664057138c53559ef63c8622a093335)
- [Deps] update `define-data-property` [`d727e7c`](https://github.com/ljharb/set-function-length/commit/d727e7c6c9a40d7bf26797694e500ea68741feea)

## [v1.0.1](https://github.com/ljharb/set-function-length/compare/v1.0.0...v1.0.1) - 2023-10-12

### Commits

- [Refactor] use `get-intrinsic`, since it‘s in the dep graph anyways [`278a954`](https://github.com/ljharb/set-function-length/commit/278a954a06cd849051c569ff7aee56df6798933e)
- [meta] add `exports` [`72acfe5`](https://github.com/ljharb/set-function-length/commit/72acfe5a0310071fb205a72caba5ecbab24336a0)

## v1.0.0 - 2023-10-12

### Commits

- Initial implementation, tests, readme [`fce14e1`](https://github.com/ljharb/set-function-length/commit/fce14e17586460e4f294405173be72b6ffdf7e5f)
- Initial commit [`ca7ba85`](https://github.com/ljharb/set-function-length/commit/ca7ba857c7c283f9d26e21f14e71cd388f2cb722)
- npm init [`6a7e493`](https://github.com/ljharb/set-function-length/commit/6a7e493927736cebcaf5c1a84e69b8e6b7b744d8)
- Only apps should have lockfiles [`d2bf6c4`](https://github.com/ljharb/set-function-length/commit/d2bf6c43de8a51b02a0aa53e8d62cb50c4a2b0da)


code:
# set-function-length <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]

[![npm badge][npm-badge-png]][package-url]

Set a function’s length.

Arguments:
 - `fn`: the function
 - `length`: the new length. Must be an integer between 0 and 2**32.
 - `loose`: Optional. If true, and the length fails to be set, do not throw. Default false.

Returns `fn`.

## Usage

```javascript
var setFunctionLength = require('set-function-length');
var assert = require('assert');

function zero() {}
function one(_) {}
function two(_, __) {}

assert.equal(zero.length, 0);
assert.equal(one.length, 1);
assert.equal(two.length, 2);

assert.equal(setFunctionLength(zero, 10), zero);
assert.equal(setFunctionLength(one, 11), one);
assert.equal(setFunctionLength(two, 12), two);

assert.equal(zero.length, 10);
assert.equal(one.length, 11);
assert.equal(two.length, 12);
```

[package-url]: https://npmjs.org/package/set-function-length
[npm-version-svg]: https://versionbadg.es/ljharb/set-function-length.svg
[deps-svg]: https://david-dm.org/ljharb/set-function-length.svg
[deps-url]: https://david-dm.org/ljharb/set-function-length
[dev-deps-svg]: https://david-dm.org/ljharb/set-function-length/dev-status.svg
[dev-deps-url]: https://david-dm.org/ljharb/set-function-length#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/set-function-length.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/set-function-length.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/set-function-length.svg
[downloads-url]: https://npm-stat.com/charts.html?package=set-function-length
[codecov-image]: https://codecov.io/gh/ljharb/set-function-length/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/ljharb/set-function-length/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/set-function-length
[actions-url]: https://github.com/ljharb/set-function-length/actions


code:
declare const env: {
	__proto__: null,
	boundFnsHaveConfigurableLengths: boolean;
	boundFnsHaveWritableLengths: boolean;
	functionsHaveConfigurableLengths: boolean;
	functionsHaveWritableLengths: boolean;
};

export = env;


code:
'use strict';

var gOPD = require('gopd');
var bind = require('function-bind');

var unbound = gOPD && gOPD(function () {}, 'length');
// @ts-expect-error ts(2555) TS is overly strict with .call
var bound = gOPD && gOPD(bind.call(function () {}), 'length');

var functionsHaveConfigurableLengths = !!(unbound && unbound.configurable);

var functionsHaveWritableLengths = !!(unbound && unbound.writable);

var boundFnsHaveConfigurableLengths = !!(bound && bound.configurable);

var boundFnsHaveWritableLengths = !!(bound && bound.writable);

/** @type {import('./env')} */
module.exports = {
	__proto__: null,
	boundFnsHaveConfigurableLengths: boundFnsHaveConfigurableLengths,
	boundFnsHaveWritableLengths: boundFnsHaveWritableLengths,
	functionsHaveConfigurableLengths: functionsHaveConfigurableLengths,
	functionsHaveWritableLengths: functionsHaveWritableLengths
};


code:
declare namespace setFunctionLength {
    type Func = (...args: unknown[]) => unknown;
}

declare function setFunctionLength<T extends setFunctionLength.Func = setFunctionLength.Func>(fn: T, length: number, loose?: boolean): T;

export = setFunctionLength;


code:
'use strict';

var GetIntrinsic = require('get-intrinsic');
var define = require('define-data-property');
var hasDescriptors = require('has-property-descriptors')();
var gOPD = require('gopd');

var $TypeError = require('es-errors/type');
var $floor = GetIntrinsic('%Math.floor%');

/** @type {import('.')} */
module.exports = function setFunctionLength(fn, length) {
	if (typeof fn !== 'function') {
		throw new $TypeError('`fn` is not a function');
	}
	if (typeof length !== 'number' || length < 0 || length > 0xFFFFFFFF || $floor(length) !== length) {
		throw new $TypeError('`length` must be a positive 32-bit integer');
	}

	var loose = arguments.length > 2 && !!arguments[2];

	var functionLengthIsConfigurable = true;
	var functionLengthIsWritable = true;
	if ('length' in fn && gOPD) {
		var desc = gOPD(fn, 'length');
		if (desc && !desc.configurable) {
			functionLengthIsConfigurable = false;
		}
		if (desc && !desc.writable) {
			functionLengthIsWritable = false;
		}
	}

	if (functionLengthIsConfigurable || functionLengthIsWritable || !loose) {
		if (hasDescriptors) {
			define(/** @type {Parameters<define>[0]} */ (fn), 'length', length, true, true);
		} else {
			define(/** @type {Parameters<define>[0]} */ (fn), 'length', length);
		}
	}
	return fn;
};


set-function-length

description: Set a function's length property

test: test

prepublish: not-in-publish || npm run prepublishOnly

prepublishOnly: safe-publish-latest

tsc: tsc -p .

posttsc: attw -P

prelint: evalmd README.md

lint: eslint --ext=js,mjs .

postlint: npm run tsc

pretest: npm run lint

tests-only: nyc tape 'test/**/*.js'

test: npm run tests-only

posttest: aud --production

version: auto-changelog && git add CHANGELOG.md

postversion: auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f "v$(node -e "console.log(require('./package.json').version)")"

type: git

keywords: javascript ecmascript set function length function.length

author: Jordan Harband <ljharb@gmail.com>

typescript: next

output: CHANGELOG.md

template: keepachangelog

ignore: .github/workflows test


code:
{
	"extends": "@ljharb/tsconfig",
	"compilerOptions": {
		"target": "es2021",
	},
	"exclude": [
		"coverage",
	],
}


code:
# Polyfill for `Object.setPrototypeOf`

[![NPM Version](https://img.shields.io/npm/v/setprototypeof.svg)](https://npmjs.org/package/setprototypeof)
[![NPM Downloads](https://img.shields.io/npm/dm/setprototypeof.svg)](https://npmjs.org/package/setprototypeof)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](https://github.com/standard/standard)

A simple cross platform implementation to set the prototype of an instianted object.  Supports all modern browsers and at least back to IE8.

## Usage:

```
$ npm install --save setprototypeof
```

```javascript
var setPrototypeOf = require('setprototypeof')

var obj = {}
setPrototypeOf(obj, {
  foo: function () {
    return 'bar'
  }
})
obj.foo() // bar
```

TypeScript is also supported:

```typescript
import setPrototypeOf from 'setprototypeof'
```


code:
declare function setPrototypeOf(o: any, proto: object | null): any;
export = setPrototypeOf;


code:
'use strict'
/* eslint no-proto: 0 */
module.exports = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array ? setProtoOf : mixinProperties)

function setProtoOf (obj, proto) {
  obj.__proto__ = proto
  return obj
}

function mixinProperties (obj, proto) {
  for (var prop in proto) {
    if (!Object.prototype.hasOwnProperty.call(obj, prop)) {
      obj[prop] = proto[prop]
    }
  }
  return obj
}


setprototypeof

description: A small polyfill for Object.setprototypeof

test: standard && mocha

testallversions: npm run node010 && npm run node4 && npm run node6 && npm run node9 && npm run node11

testversion: docker run -it --rm -v $(PWD):/usr/src/app -w /usr/src/app node:${NODE_VER} npm install mocha@${MOCHA_VER:-latest} && npm t

node010: NODE_VER=0.10 MOCHA_VER=3 npm run testversion

node4: NODE_VER=4 npm run testversion

node6: NODE_VER=6 npm run testversion

node9: NODE_VER=9 npm run testversion

node11: NODE_VER=11 npm run testversion

prepublishOnly: npm t

postpublish: git push origin && git push origin --tags

keywords: polyfill object setprototypeof

author: Wes Todd

license: ISC


code:
'use strict'
/* eslint-env mocha */
/* eslint no-proto: 0 */
var assert = require('assert')
var setPrototypeOf = require('..')

describe('setProtoOf(obj, proto)', function () {
  it('should merge objects', function () {
    var obj = { a: 1, b: 2 }
    var proto = { b: 3, c: 4 }
    var mergeObj = setPrototypeOf(obj, proto)

    if (Object.getPrototypeOf) {
      assert.strictEqual(Object.getPrototypeOf(obj), proto)
    } else if ({ __proto__: [] } instanceof Array) {
      assert.strictEqual(obj.__proto__, proto)
    } else {
      assert.strictEqual(obj.a, 1)
      assert.strictEqual(obj.b, 2)
      assert.strictEqual(obj.c, 4)
    }
    assert.strictEqual(mergeObj, obj)
  })
})


code:
# shallow-clone [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/shallow-clone.svg?style=flat)](https://www.npmjs.com/package/shallow-clone) [![NPM monthly downloads](https://img.shields.io/npm/dm/shallow-clone.svg?style=flat)](https://npmjs.org/package/shallow-clone) [![NPM total downloads](https://img.shields.io/npm/dt/shallow-clone.svg?style=flat)](https://npmjs.org/package/shallow-clone) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/shallow-clone.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/shallow-clone)

> Creates a shallow clone of any JavaScript value.

Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.

## Install

Install with [npm](https://www.npmjs.com/):

```sh
$ npm install --save shallow-clone
```

## Usage

```js
const clone = require('shallow-clone');
```

**Supports**

* array buffers
* arrays
* buffers
* dates
* errors
* float32 arrays
* float64 arrays
* int16 arrays
* int32 arrays
* int8 arrays
* maps
* objects
* primitives
* regular expressions
* sets
* symbols
* uint16 arrays
* uint32 arrays
* uint8 arrays
* uint8clamped arrays

## Arrays

By default, only the array itself is cloned (shallow), use [clone-deep](https://github.com/jonschlinkert/clone-deep) if you also need the elements in the array to be cloned.

```js
const arr = [{ a: 0 }, { b: 1 }];
const foo = clone(arr);
// foo =>  [{ 'a': 0 }, { 'b': 1 }]

// array is cloned
assert(actual === expected); // false

// array elements are not
assert.deepEqual(actual[0], expected[0]); // true
```

## Objects

Only the object is shallow cloned, use [clone-deep](https://github.com/jonschlinkert/clone-deep) if you also need the values in the object to be cloned.

```js
console.log(clone({ a: 1, b: 2, c: 3 }));
//=> {a: 1, b: 2, c: 3 }
```

## RegExp

Clones regular expressions and flags, and preserves the `.lastIndex`.

```js
const regex = clone(/foo/g); //=> /foo/g
// you can manually reset lastIndex if necessary
regex.lastIndex = 0;
```

## Primitives

Simply returns primitives unchanged.

```js
clone(0); //=> 0
clone('foo'); //=> 'foo'
```

## About

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).

Running Tests

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

```sh
$ npm install && npm test
```

Building docs

_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_

To generate the readme, run the following command:

```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```

### Related projects

You might also be interested in these projects:

* [assign-deep](https://www.npmjs.com/package/assign-deep): Deeply assign the values of all enumerable-own-properties and symbols from one or more source objects… [more](https://github.com/jonschlinkert/assign-deep) | [homepage](https://github.com/jonschlinkert/assign-deep "Deeply assign the values of all enumerable-own-properties and symbols from one or more source objects to a target object. Returns the target object.")
* [clone-deep](https://www.npmjs.com/package/clone-deep): Recursively (deep) clone JavaScript native types, like Object, Array, RegExp, Date as well as primitives. | [homepage](https://github.com/jonschlinkert/clone-deep "Recursively (deep) clone JavaScript native types, like Object, Array, RegExp, Date as well as primitives.")
* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object "Returns true if an object was created by the `Object` constructor.")
* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of "Get the native type of a value.")

### Contributors

| **Commits** | **Contributor** |
| --- | --- |
| 20 | [jonschlinkert](https://github.com/jonschlinkert) |
| 2  | [doowb](https://github.com/doowb) |
| 1  | [jakub-g](https://github.com/jakub-g) |

### Author

**Jon Schlinkert**

* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)

### License

Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).

***

_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 15, 2019._


code:
/*!
 * shallow-clone <https://github.com/jonschlinkert/shallow-clone>
 *
 * Copyright (c) 2015-present, Jon Schlinkert.
 * Released under the MIT License.
 */

'use strict';

const valueOf = Symbol.prototype.valueOf;
const typeOf = require('kind-of');

function clone(val, deep) {
  switch (typeOf(val)) {
    case 'array':
      return val.slice();
    case 'object':
      return Object.assign({}, val);
    case 'date':
      return new val.constructor(Number(val));
    case 'map':
      return new Map(val);
    case 'set':
      return new Set(val);
    case 'buffer':
      return cloneBuffer(val);
    case 'symbol':
      return cloneSymbol(val);
    case 'arraybuffer':
      return cloneArrayBuffer(val);
    case 'float32array':
    case 'float64array':
    case 'int16array':
    case 'int32array':
    case 'int8array':
    case 'uint16array':
    case 'uint32array':
    case 'uint8clampedarray':
    case 'uint8array':
      return cloneTypedArray(val);
    case 'regexp':
      return cloneRegExp(val);
    case 'error':
      return Object.create(val);
    default: {
      return val;
    }
  }
}

function cloneRegExp(val) {
  const flags = val.flags !== void 0 ? val.flags : (/\w+$/.exec(val) || void 0);
  const re = new val.constructor(val.source, flags);
  re.lastIndex = val.lastIndex;
  return re;
}

function cloneArrayBuffer(val) {
  const res = new val.constructor(val.byteLength);
  new Uint8Array(res).set(new Uint8Array(val));
  return res;
}

function cloneTypedArray(val, deep) {
  return new val.constructor(val.buffer, val.byteOffset, val.length);
}

function cloneBuffer(val) {
  const len = val.length;
  const buf = Buffer.allocUnsafe ? Buffer.allocUnsafe(len) : Buffer.from(len);
  val.copy(buf);
  return buf;
}

function cloneSymbol(val) {
  return valueOf ? Object(valueOf.call(val)) : {};
}

/**
 * Expose `clone`
 */

module.exports = clone;


shallow-clone

description: Creates a shallow clone of any JavaScript value.

repository: jonschlinkert/shallow-clone

test: mocha

keywords: array clone copy extend mixin object primitive shallow

layout: default

list: assign-deep clone-deep is-plain-object kind-of


code:
'use strict';
const shebangRegex = require('shebang-regex');

module.exports = (string = '') => {
	const match = string.match(shebangRegex);

	if (!match) {
		return null;
	}

	const [path, argument] = match[0].replace(/#! ?/, '').split(' ');
	const binary = path.split('/').pop();

	if (binary === 'env') {
		return argument;
	}

	return argument ? `${binary} ${argument}` : binary;
};


shebang-command

description: Get the command from a shebang

repository: kevva/shebang-command

test: xo && ava

keywords: cmd command parse shebang


code:
# shebang-command [![Build Status](https://travis-ci.org/kevva/shebang-command.svg?branch=master)](https://travis-ci.org/kevva/shebang-command)

> Get the command from a shebang

## Install

```
$ npm install shebang-command
```

## Usage

```js
const shebangCommand = require('shebang-command');

shebangCommand('#!/usr/bin/env node');
//=> 'node'

shebangCommand('#!/bin/bash');
//=> 'bash'
```

## API

### shebangCommand(string)

#### string

Type: `string`

String containing a shebang.


code:
/**
Regular expression for matching a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line.

@example
```
import shebangRegex = require('shebang-regex');

const string = '#!/usr/bin/env node\nconsole.log("unicorns");';

shebangRegex.test(string);
//=> true

shebangRegex.exec(string)[0];
//=> '#!/usr/bin/env node'

shebangRegex.exec(string)[1];
//=> '/usr/bin/env node'
```
*/
declare const shebangRegex: RegExp;

export = shebangRegex;


code:
'use strict';
module.exports = /^#!(.*)/;


shebang-regex

description: Regular expression for matching a shebang line

repository: sindresorhus/shebang-regex

test: xo && ava && tsd

keywords: regex regexp shebang match test line


code:
# shebang-regex [![Build Status](https://travis-ci.org/sindresorhus/shebang-regex.svg?branch=master)](https://travis-ci.org/sindresorhus/shebang-regex)

> Regular expression for matching a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line

## Install

```
$ npm install shebang-regex
```

## Usage

```js
const shebangRegex = require('shebang-regex');

const string = '#!/usr/bin/env node\nconsole.log("unicorns");';

shebangRegex.test(string);
//=> true

shebangRegex.exec(string)[0];
//=> '#!/usr/bin/env node'

shebangRegex.exec(string)[1];
//=> '/usr/bin/env node'
```

## License

MIT © [Sindre Sorhus](https://sindresorhus.com)


code:
{
	"root": true,

	"extends": "@ljharb",

	"rules": {
		"array-bracket-newline": 0,
		"complexity": 0,
		"eqeqeq": 1,
		"func-style": [2, "declaration"],
		"max-depth": 0,
		"max-lines-per-function": 0,
		"max-statements": 0,
		"multiline-comment-style": 0,
		"no-negated-condition": 1,
		"no-param-reassign": 1,
    "no-lonely-if": 1,
		"no-shadow": 1,
		"no-template-curly-in-string": 0,
	},

	"overrides": [
		{
			"files": "example/**",
			"rules": {
				"no-console": 0,
			},
		},
	],
}


code:
# These are supported funding model platforms

github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/shell-quote
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


exclude: coverage example test


code:
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.8.1](https://github.com/ljharb/shell-quote/compare/v1.8.0...v1.8.1) - 2023-04-07

### Fixed

- [Fix] `parse`: preserve whitespace in comments [`#6`](https://github.com/ljharb/shell-quote/issues/6)
- [Fix] properly support the `escape` option [`#5`](https://github.com/ljharb/shell-quote/issues/5)

### Commits

- [Refactor] `parse`: hoist `getVar` to module level [`b42ac73`](https://github.com/ljharb/shell-quote/commit/b42ac73e39e566cfc355a4addc4bd2df2652556c)
- [Refactor] hoist some vars to module level [`8f0c5c3`](https://github.com/ljharb/shell-quote/commit/8f0c5c3c9df3a10e32f1972636675af6fffef998)
- [Refactor] `parse`: use `slice` over `substr`, cache some values [`fcb2e1a`](https://github.com/ljharb/shell-quote/commit/fcb2e1acd5312a1a1a4e6c66ec688aab383023b5)
- [Refactor] `parse`: a bit of cleanup [`6780ec5`](https://github.com/ljharb/shell-quote/commit/6780ec5194e36e2a696bfbaaf85169682a333321)
- [Refactor] `parse`: tweak the regex to not match nothing [`227d474`](https://github.com/ljharb/shell-quote/commit/227d4742a006e81ec3fde1eee103731a6f7ea920)
- [Tests] increase coverage [`a66de94`](https://github.com/ljharb/shell-quote/commit/a66de943555e49fbb1b657cbe3c5b2c703ae507d)
- [Refactor] `parse`: avoid shadowing a function arg [`1d58679`](https://github.com/ljharb/shell-quote/commit/1d5867907ecbf553556fe6ad790b6d6658aedba3)

## [v1.8.0](https://github.com/ljharb/shell-quote/compare/v1.7.4...v1.8.0) - 2023-01-30

### Commits

- [New] extract `parse` and `quote` to their own deep imports [`553fdfc`](https://github.com/ljharb/shell-quote/commit/553fdfc32cc41b4c2f77e061b6957703958ca575)
- [Tests] add `nyc` coverage [`fd7ddcd`](https://github.com/ljharb/shell-quote/commit/fd7ddcdd84bfef064c6d9a06b055a95531b26897)
- [New] Add support for here strings (`<<<`) [`9802fb3`](https://github.com/ljharb/shell-quote/commit/9802fb37c7946e18c672b81122520dc296bde271)
- [New] `parse`: Add syntax support for duplicating input file descriptors [`216b198`](https://github.com/ljharb/shell-quote/commit/216b19894f76b14d164c4c5a68f05a51b06336c4)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`85f8e31`](https://github.com/ljharb/shell-quote/commit/85f8e31dd80e1dde63d58204b653e497a53857e6)
- [Tests] add `evalmd` [`c5549fc`](https://github.com/ljharb/shell-quote/commit/c5549fcd82d70046bdc2b1c34184ae9f9d0191f9)
- [actions] update checkout action [`62e9b49`](https://github.com/ljharb/shell-quote/commit/62e9b4958cfa2f9009b7069076612fe33528c1fb)

## [v1.7.4](https://github.com/ljharb/shell-quote/compare/1.7.3...v1.7.4) - 2022-10-12

### Merged

- Add node_modules to .gitignore [`#48`](https://github.com/ljharb/shell-quote/pull/48)

### Commits

- [eslint] fix indentation and whitespace [`aaa9d1f`](https://github.com/ljharb/shell-quote/commit/aaa9d1f65bf3445e6af1efaa4a8f8c13a21aa593)
- [eslint] additional cleanup [`397cb62`](https://github.com/ljharb/shell-quote/commit/397cb628f3d96e4e47763147c0d6074997a13880)
- [meta] add `auto-changelog` [`497fca5`](https://github.com/ljharb/shell-quote/commit/497fca509af3b7d6daaba459bad1f45ac0af3ff1)
- [actions] add reusable workflows [`4763c36`](https://github.com/ljharb/shell-quote/commit/4763c36274c5881a2d141ce9f2b17b7d1d95e8cd)
- [eslint] add eslint [`6ee1437`](https://github.com/ljharb/shell-quote/commit/6ee1437df1b10a79bdf2aaa04f2bacc9f420dc15)
- [readme] rename, add badges [`7eb5134`](https://github.com/ljharb/shell-quote/commit/7eb513483d931602452ec572ed456714148acd2b)
- [meta] update URLs [`67381b6`](https://github.com/ljharb/shell-quote/commit/67381b61fa95e57819333463f491428747893186)
- [meta] create FUNDING.yml; add `funding` in package.json [`8641572`](https://github.com/ljharb/shell-quote/commit/86415722d875578adf1f95f9e649ba42c805bc32)
- [meta] use `npmignore` to autogenerate an npmignore file [`2e2007a`](https://github.com/ljharb/shell-quote/commit/2e2007a393f90bf079fc556a921120b3508c4fc3)
- Only apps should have lockfiles [`f97411e`](https://github.com/ljharb/shell-quote/commit/f97411ef4d2f183200fc8a28beca9faf9b08a640)
- [Dev Deps] update `tape` [`051f608`](https://github.com/ljharb/shell-quote/commit/051f60857ad5035280208abdc348bf5ba42a6254)
- [meta] add `safe-publish-latest` [`18cadf9`](https://github.com/ljharb/shell-quote/commit/18cadf95357392fcd78ea8619956fd41eed62649)
- [Tests] add `aud` in `posttest` [`dc1cc12`](https://github.com/ljharb/shell-quote/commit/dc1cc12b956ccd93d58aaaad263bee7d50576d27)

<!-- auto-changelog-above -->

## 1.7.3
* Fix a security issue where the regex for windows drive letters allowed some shell meta-characters
to escape the quoting rules. (CVE-2021-42740)

## 1.7.2
* Fix a regression introduced in 1.6.3. This reverts the Windows path quoting fix. ([144e1c2](https://github.com/ljharb/shell-quote/commit/144e1c20cd57549a414c827fb3032e60b7b8721c))

## 1.7.1
* Fix `$` being removed when not part of an environment variable name. ([@Adman](https://github.com/Admin) in [#32](https://github.com/ljharb/shell-quote/pull/32))

## 1.7.0
* Add support for parsing `>>` and `>&` redirection operators. ([@forivall](https://github.com/forivall) in [#16](https://github.com/ljharb/shell-quote/pull/16))
* Add support for parsing `<(` process substitution operator. ([@cuonglm](https://github.com/cuonglm) in [#15](https://github.com/ljharb/shell-quote/pull/15))

## 1.6.3
* Fix Windows path quoting problems. ([@dy](https://github.com/dy) in [#34](https://github.com/ljharb/shell-quote/pull/34))

## [v1.6.2](https://github.com/ljharb/shell-quote/compare/1.6.1...v1.6.2) - 2019-08-13

### Merged

- Use native JSON and Array methods [`#21`](https://github.com/ljharb/shell-quote/pull/21)

### Commits

- fix whitespace [`72fb5a8`](https://github.com/ljharb/shell-quote/commit/72fb5a8ce29b4f67f28302af33c217b58f92e260)
- Disable package-lock.json [`d450577`](https://github.com/ljharb/shell-quote/commit/d4505770b2a4251af2da8e177385c5e0456a83b6)

## [1.6.1](https://github.com/ljharb/shell-quote/compare/1.6.0...1.6.1) - 2016-06-17

### Commits

- Fix some more escaping for .quote() [`ace52f4`](https://github.com/ljharb/shell-quote/commit/ace52f4c8717b370b301a3db3a4727db26e309ad)
- Fix escaping for greater than and less than [`70e9eb2`](https://github.com/ljharb/shell-quote/commit/70e9eb2a854eb56a3dfa255be12610a722bbe080)

## [1.6.0](https://github.com/ljharb/shell-quote/compare/1.5.0...1.6.0) - 2016-04-23

### Commits

- add comment parsing feature [`b8b5c31`](https://github.com/ljharb/shell-quote/commit/b8b5c31c16a15aa4ab26c8f23d362a24b9fa57c4)

## [1.5.0](https://github.com/ljharb/shell-quote/compare/1.4.3...1.5.0) - 2016-03-16

### Commits

- add escape option to .parse [`4d400e7`](https://github.com/ljharb/shell-quote/commit/4d400e773be448c320b6dc9b2eb1323d7a3461ca)

## [1.4.3](https://github.com/ljharb/shell-quote/compare/1.4.2...1.4.3) - 2015-03-07

### Commits

- Fix quote() with special chars [`811b5a0`](https://github.com/ljharb/shell-quote/commit/811b5a0aff79f347db245edcf88750977c111844)

## [1.4.2](https://github.com/ljharb/shell-quote/compare/1.4.1...1.4.2) - 2014-07-20

### Commits

- Handle non-strings when quoting [`d435827`](https://github.com/ljharb/shell-quote/commit/d43582741c5599807249c28722487aa86bb16f06)
- falseys ok [`22dbd94`](https://github.com/ljharb/shell-quote/commit/22dbd9492c372038d439d6ec08c6288ca5fa3c10)
- all the falseys test [`c99dca5`](https://github.com/ljharb/shell-quote/commit/c99dca59dca64743877a0411d299ce669f0a2d1d)

## [1.4.1](https://github.com/ljharb/shell-quote/compare/1.4.0...1.4.1) - 2013-12-24

### Commits

- es5 shims [`00dc6ab`](https://github.com/ljharb/shell-quote/commit/00dc6abfdd2f3ff2908616dbe7b6584bbf1b0e24)
- separate shim file to get the coverage up [`e29a216`](https://github.com/ljharb/shell-quote/commit/e29a2167319913af3f26603bc33938bb9be1d74d)
- use array-{filter,map,reduce} [`97a2fc9`](https://github.com/ljharb/shell-quote/commit/97a2fc9c92917343a33662b3705860e4f2044730)
- add testling badge [`44c98b1`](https://github.com/ljharb/shell-quote/commit/44c98b1e341d348ce9b5b4d78bb4d26345e868ea)
- upgrade tape [`3fc22d3`](https://github.com/ljharb/shell-quote/commit/3fc22d3d38592e6fc3b3308cc73a282d641bad34)

## [1.4.0](https://github.com/ljharb/shell-quote/compare/1.3.3...1.4.0) - 2013-10-17

### Merged

- Add MIT LICENSE file [`#6`](https://github.com/ljharb/shell-quote/pull/6)

### Commits

- Rewrite parser as a character based scanner [`c7ca9a2`](https://github.com/ljharb/shell-quote/commit/c7ca9a200350c02fb86c4222b63f15b54a0a0226)
- Add tests for glob patterns [`3418892`](https://github.com/ljharb/shell-quote/commit/3418892031b126197302eb57cc92a729b740fac6)
- Update algo description [`e1442cf`](https://github.com/ljharb/shell-quote/commit/e1442cfe0521497b59a8204eb1e4d6c4202d42b9)
- Fix test case for backslash in double quotes [`89bc550`](https://github.com/ljharb/shell-quote/commit/89bc5500711643e87fe93dd1bde0e8745c34d733)
- Add failing tests for crazy quoting tricks [`58a5e48`](https://github.com/ljharb/shell-quote/commit/58a5e4800a62fdc3e980feae1e6c6b15c812f0cb)

## [1.3.3](https://github.com/ljharb/shell-quote/compare/1.3.2...1.3.3) - 2013-06-24

### Commits

- failing set test with an env cb [`9fb2096`](https://github.com/ljharb/shell-quote/commit/9fb20968b407c590745a982d2a562960e952142d)
- remove the broken special case [`f9a0ee5`](https://github.com/ljharb/shell-quote/commit/f9a0ee574f9d5e5d5b382f55da960c23eb7d44c5)

## [1.3.2](https://github.com/ljharb/shell-quote/compare/1.3.1...1.3.2) - 2013-06-24

### Commits

- tests for setting env vars [`f44b039`](https://github.com/ljharb/shell-quote/commit/f44b03906c60598470676edcabea79c4f7488407)
- fixed the parse test, broke the op tests [`74d6686`](https://github.com/ljharb/shell-quote/commit/74d66863615a60fcb222d2279991cff3a89ff015)
- factored out single and double quote regex [`de9e0a5`](https://github.com/ljharb/shell-quote/commit/de9e0a5081156e5483b5df24878c7414f90ec67e)
- updated set env test, already passes [`7d5636b`](https://github.com/ljharb/shell-quote/commit/7d5636bec5e76ff542712e07643b09c597d20b21)
- ops fixed [`2b4e1b1`](https://github.com/ljharb/shell-quote/commit/2b4e1b1fb63519456c7850d365e1ffe5fa5972b2)
- passing all tests [`44177e3`](https://github.com/ljharb/shell-quote/commit/44177e3dcbd96dfa331483eba0abbfb0291c130f)
- backreferences in negated capture groups don't actually work [`e189d9d`](https://github.com/ljharb/shell-quote/commit/e189d9d5910c6ecc7d564309ca9e110062f9589e)
- another crazy ridiculous passing parse test [`d1beb6b`](https://github.com/ljharb/shell-quote/commit/d1beb6b32ec7ad8752b305834a21c800cae74a95)
- failing test for quoted whitespace and nested quotes [`9a4c11c`](https://github.com/ljharb/shell-quote/commit/9a4c11cba0f61762aaa7887591d78fe7e965cf65)
- failing test for quotes embedded inside barewords [`d997384`](https://github.com/ljharb/shell-quote/commit/d997384018ce107ab8e12aa5b8d8359c2f77128b)

## [1.3.1](https://github.com/ljharb/shell-quote/compare/1.3.0...1.3.1) - 2013-05-13

### Commits

- pass objects through [`f9c0514`](https://github.com/ljharb/shell-quote/commit/f9c0514abbdf8ba16fafb68736863d14b39015ef)

## [1.3.0](https://github.com/ljharb/shell-quote/compare/1.2.0...1.3.0) - 2013-05-13

### Commits

- hacky tokenizer is much simpler [`7e91b18`](https://github.com/ljharb/shell-quote/commit/7e91b18d1cf3fffd6a9c5f69d785f200c0c81b66)
- nearly passing with a clunky state env parser, array issues [`d6d6416`](https://github.com/ljharb/shell-quote/commit/d6d64160f2fc8a23018410ffe84ab7f1b0c4fa02)
- test for functional env expansion [`666395f`](https://github.com/ljharb/shell-quote/commit/666395f9f195241c6077f242dc4f2851bed95f8d)
- upgrade travis versions, tape [`f6f8bd6`](https://github.com/ljharb/shell-quote/commit/f6f8bd6026375d44d40c7f2e1fead43d006be211)
- 1.3.0, document env() lookups [`041c5da`](https://github.com/ljharb/shell-quote/commit/041c5da88800b4e15f0ed023049050b11b623a23)
- first half of functional env() works [`7a0cf79`](https://github.com/ljharb/shell-quote/commit/7a0cf79987fbdcc00d8f36c6dc164d22db963d23)
- env() objects even work inside quote strings [`16139f5`](https://github.com/ljharb/shell-quote/commit/16139f52bf7a2beb7e1ca9b61b93a9ea598b0f1a)
- another check just to make sure env() works [`914a1a9`](https://github.com/ljharb/shell-quote/commit/914a1a9ec55cd76bedfed4086c35866733128036)

## [1.2.0](https://github.com/ljharb/shell-quote/compare/1.1.0...1.2.0) - 2013-05-13

### Commits

- failing test for special shell parameter env vars [`728862a`](https://github.com/ljharb/shell-quote/commit/728862a6ff246754083da5cf22322caf914ae990)
- add the special vars to the replace regex but the chunker breaks on them [`d1ff82a`](https://github.com/ljharb/shell-quote/commit/d1ff82a07c44cb53ab909b61833296f38257eabd)
- fixed the env test, everything is fine [`a45897f`](https://github.com/ljharb/shell-quote/commit/a45897f53ba184a77bc762c63777b95590a83962)

## [1.1.0](https://github.com/ljharb/shell-quote/compare/1.0.0...1.1.0) - 2013-05-13

### Commits

- quote all ops objects [`ac7be63`](https://github.com/ljharb/shell-quote/commit/ac7be63574e1da48bc6f495aee363d31863222c3)
- test for parsed ops objects in quote() [`59fb71b`](https://github.com/ljharb/shell-quote/commit/59fb71b39c53b83306d015bec62fc93667745f75)
- another test for op object quoting [`5819a31`](https://github.com/ljharb/shell-quote/commit/5819a31a19c34967dcb7bd1719250ed2aa480583)

## [1.0.0](https://github.com/ljharb/shell-quote/compare/0.1.1...1.0.0) - 2013-05-13

### Commits

- document ops, op example [`a6381e6`](https://github.com/ljharb/shell-quote/commit/a6381e612361148a8433c6ec4891aabc4649cb40)
- some more passing double-char op tests [`fbc6e5c`](https://github.com/ljharb/shell-quote/commit/fbc6e5c40858ef4ea4d69651ac8fdf6c0c780eed)
- failing test for | and & ops [`d817736`](https://github.com/ljharb/shell-quote/commit/d81773643cbc2e25576884d606165dc87e8bbfac)
- labeled regex states [`8c008b2`](https://github.com/ljharb/shell-quote/commit/8c008b223e6174d6bec098251527053e5cc1f30c)
- refactored the chunker regex into a string [`0331c7f`](https://github.com/ljharb/shell-quote/commit/0331c7f63077fda116b3c73540b71880538a4391)
- simple failing double-char op test [`e51fa90`](https://github.com/ljharb/shell-quote/commit/e51fa90f854063a408e8c1645b385c1ed42c72c6)
- failing expanded single-op tests for ; and () [`710bb24`](https://github.com/ljharb/shell-quote/commit/710bb243f23d4a55158688b71cb56b67f66ea99f)
- now passing all the single-char op tests [`e3e9ac1`](https://github.com/ljharb/shell-quote/commit/e3e9ac17ef02300bad7f4faefee5c7a993b3bc97)
- using the control ops directly from the docs [`f535987`](https://github.com/ljharb/shell-quote/commit/f53598732ba606c7bca66fd7d55d809544c452cf)
- first part of op parsing works [`e6f9199`](https://github.com/ljharb/shell-quote/commit/e6f91991fe437eae6b7e4f571843b3d48c746aeb)
- failing redirect tests [`cb94c10`](https://github.com/ljharb/shell-quote/commit/cb94c105a4e32fac2d356b956f53aff999ae88e8)
- another double-char op test just to be sure [`5cf1bf2`](https://github.com/ljharb/shell-quote/commit/5cf1bf29e3324a6cc1e40c01c4529b28ca0b47a5)
- 1.0.0 for ops [`17a40ed`](https://github.com/ljharb/shell-quote/commit/17a40edb3cd7a0f1c44be2be5ddd412c8ca2b7ca)
- adding redirect <> ops to CONTROL makes the tests pass [`48b1eb9`](https://github.com/ljharb/shell-quote/commit/48b1eb97cfa306659de66bd29615051a3644b9ce)
- double-char op test now passing [`3998b0f`](https://github.com/ljharb/shell-quote/commit/3998b0f9ecb32883f8eb3be31110a84d276ac764)
- using the meta chars directly from the docs [`b009ef6`](https://github.com/ljharb/shell-quote/commit/b009ef6d04eb1cc57d66cf3670d24e03fa0fc6bd)
- the spec says tabs are also allowed [`2adb373`](https://github.com/ljharb/shell-quote/commit/2adb37366bdfae198ce61e4658e513d3e0bc98fa)
- op test completely passing [`20a0147`](https://github.com/ljharb/shell-quote/commit/20a01475741d9fba801bbd2b0c1a5f215dc9cec4)

## [0.1.1](https://github.com/ljharb/shell-quote/compare/0.1.0...0.1.1) - 2013-04-17

### Commits

- Return empty list when parsing an empty (or whitespace-only) string [`1475717`](https://github.com/ljharb/shell-quote/commit/14757177ead209f5ae3c9d4a3020fba9f522725f)

## [0.1.0](https://github.com/ljharb/shell-quote/compare/0.0.1...0.1.0) - 2013-04-14

### Commits

- externalize the regex declaration [`37d6058`](https://github.com/ljharb/shell-quote/commit/37d60580a4a4656ff836c4a2ecdd7282705ffd27)
- modernize the readme [`24106f5`](https://github.com/ljharb/shell-quote/commit/24106f5c81ab83bddb2bf735cad60e99e1494dcf)
- factor out interpolation [`1b21b01`](https://github.com/ljharb/shell-quote/commit/1b21b018e01392d2c74e53136f8fa0ca838d7643)
- half the env tests are working with basic interpolation [`5891471`](https://github.com/ljharb/shell-quote/commit/589147176be93834bad7fcee83bd255e35c14adc)
- env parse example [`5757c42`](https://github.com/ljharb/shell-quote/commit/5757c4256a4cbaeaabd3d8cd91c7e19109329067)
- failing tests for unimplemented env interpolation [`590534a`](https://github.com/ljharb/shell-quote/commit/590534ae8eced91974a4be8f2b6bc7dcb53e3211)
- denormalize the interpolate logic to make room for special cases [`c669d2e`](https://github.com/ljharb/shell-quote/commit/c669d2e8b84e0eb0dde1798eae7c37a21dc5b7a1)
- cleaner implementation recursing on the double quote case [`adae66f`](https://github.com/ljharb/shell-quote/commit/adae66f47cf11bb7a686c98059436f496d881955)
- one test was wrong, checking for pre escapes [`42b5f83`](https://github.com/ljharb/shell-quote/commit/42b5f8355196d5f2a2c40178576a5d37167e8cb2)
- finally passing all the tests [`efa4084`](https://github.com/ljharb/shell-quote/commit/efa408481db33993fce2a1dd3c15ffac0203fe4c)
- one more test passing with quote recursion [`e9537b9`](https://github.com/ljharb/shell-quote/commit/e9537b943d89535b38c0777c210b5aa9780349b2)
- use tape everywhere [`ed0c1c6`](https://github.com/ljharb/shell-quote/commit/ed0c1c6ae383998874002ae9aa452505c266d630)
- some extra metacharacter tests just to be sure [`a6782ae`](https://github.com/ljharb/shell-quote/commit/a6782aeb931221a459f9cebc371c2311ad680992)
- minor fix to an env test [`601b340`](https://github.com/ljharb/shell-quote/commit/601b3406e7012da97771b0ed538288ddb12d9af8)
- document parse env [`cc0efba`](https://github.com/ljharb/shell-quote/commit/cc0efba0bce75aaab1ead72e81470154a71ec525)
- better parse recursion to capture the containing quotes [`8467961`](https://github.com/ljharb/shell-quote/commit/84679611fd5843777d3d94f157a7b6efe11097ca)
- now just 2 tests failing with a subtle regex reordering [`5448a02`](https://github.com/ljharb/shell-quote/commit/5448a02d356722ec8ef57db2e075e10566e2dcdb)
- pass another test by using "" as the undefined [`46e6cf4`](https://github.com/ljharb/shell-quote/commit/46e6cf4b974e1cec0601e81a0dc2820dc849f775)
- fixed a failing env test [`17d1fda`](https://github.com/ljharb/shell-quote/commit/17d1fdac759e7a94ffc6edc26af27769d30e53bb)
- actually the test was wrong, module works fine [`9d7b727`](https://github.com/ljharb/shell-quote/commit/9d7b727f2911692cffe03e7792b5defad3dd75d2)
- another test to be even more sure [`5afd47b`](https://github.com/ljharb/shell-quote/commit/5afd47ba563d4c1bb7966695e100f63da4b65915)
- failing test for: echo "foo = \"foo\"" [`8dbb280`](https://github.com/ljharb/shell-quote/commit/8dbb2803136a2f7643e7e66b2d6b95f9adfbfd41)

## [0.0.1](https://github.com/ljharb/shell-quote/compare/0.0.0...0.0.1) - 2012-05-18

### Commits

- fixed unescaped metachars and bump [`5ce339f`](https://github.com/ljharb/shell-quote/commit/5ce339feeaf971a5172fe58faa3ac5f90bdfe8b5)
- failing test for unescaped metachars [`a315125`](https://github.com/ljharb/shell-quote/commit/a315125a0742b01799c89469efd20821540694f6)
- fix for escaped spaces [`669b616`](https://github.com/ljharb/shell-quote/commit/669b61610aad3e6f47e8c043e1635dcc4b5ce375)
- failing test for escaped space [`c6ff3dc`](https://github.com/ljharb/shell-quote/commit/c6ff3dc6811816a667e55a69ec09bffa52f5ee0a)

## 0.0.0 - 2012-05-18

### Commits

- readme with examples [`6373c0f`](https://github.com/ljharb/shell-quote/commit/6373c0f56de87702a61063ffae354e2bb989de91)
- package.json [`bc27efa`](https://github.com/ljharb/shell-quote/commit/bc27efa033709ede8483bb6ec0f182dcb2b87061)
- passing the parse test [`69c0f85`](https://github.com/ljharb/shell-quote/commit/69c0f8529825d3fdc700e81d32b75378cef47994)
- crazy initial thing [`d6469c9`](https://github.com/ljharb/shell-quote/commit/d6469c95adf0172adc65c4adab04910486368ee7)
- passing quote tests [`e1d6695`](https://github.com/ljharb/shell-quote/commit/e1d669503f0f159068deb45f14ba3a4bf77e90f0)
- failing parse test [`980aa58`](https://github.com/ljharb/shell-quote/commit/980aa585937d049b152b5e7b08c1e068faaaf378)
- using travis [`1c72261`](https://github.com/ljharb/shell-quote/commit/1c72261f45002744fac3fecec3f0395924d66717)
- expand more escape sequences in parse() [`8b2224c`](https://github.com/ljharb/shell-quote/commit/8b2224c465ef70d2320985c57dc0b8ee1c0a3664)


code:
# shell-quote <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]

[![npm badge][npm-badge-png]][package-url]

Parse and quote shell commands.

# example

## quote

``` js
var quote = require('shell-quote/quote');
var s = quote([ 'a', 'b c d', '$f', '"g"' ]);
console.log(s);
```

output

```
a 'b c d' \$f '"g"'
```

## parse

``` js
var parse = require('shell-quote/parse');
var xs = parse('a "b c" \\$def \'it\\\'s great\'');
console.dir(xs);
```

output

```
[ 'a', 'b c', '\\$def', 'it\'s great' ]
```

## parse with an environment variable

``` js
var parse = require('shell-quote/parse');
var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' });
console.dir(xs);
```

output

```
[ 'beep', '--boop=/home/robot' ]
```

## parse with custom escape character

``` js
var parse = require('shell-quote/parse');
var xs = parse('beep ^--boop="$PWD"', { PWD: '/home/robot' }, { escape: '^' });
console.dir(xs);
```

output

```
[ 'beep --boop=/home/robot' ]
```

## parsing shell operators

``` js
var parse = require('shell-quote/parse');
var xs = parse('beep || boop > /byte');
console.dir(xs);
```

output:

```
[ 'beep', { op: '||' }, 'boop', { op: '>' }, '/byte' ]
```

## parsing shell comment

``` js
var parse = require('shell-quote/parse');
var xs = parse('beep > boop # > kaboom');
console.dir(xs);
```

output:

```
[ 'beep', { op: '>' }, 'boop', { comment: '> kaboom' } ]
```

# methods

``` js
var quote = require('shell-quote/quote');
var parse = require('shell-quote/parse');
```

## quote(args)

Return a quoted string for the array `args` suitable for using in shell
commands.

## parse(cmd, env={})

Return an array of arguments from the quoted string `cmd`.

Interpolate embedded bash-style `$VARNAME` and `${VARNAME}` variables with
the `env` object which like bash will replace undefined variables with `""`.

`env` is usually an object but it can also be a function to perform lookups.
When `env(key)` returns a string, its result will be output just like `env[key]`
would. When `env(key)` returns an object, it will be inserted into the result
array like the operator objects.

When a bash operator is encountered, the element in the array with be an object
with an `"op"` key set to the operator string. For example:

```
'beep || boop > /byte'
```

parses as:

```
[ 'beep', { op: '||' }, 'boop', { op: '>' }, '/byte' ]
```

# install

With [npm](http://npmjs.org) do:

```
npm install shell-quote
```

# license

MIT

[package-url]: https://npmjs.org/package/shell-quote
[npm-version-svg]: https://versionbadg.es/ljharb/shell-quote.svg
[deps-svg]: https://david-dm.org/ljharb/shell-quote.svg
[deps-url]: https://david-dm.org/ljharb/shell-quote
[dev-deps-svg]: https://david-dm.org/ljharb/shell-quote/dev-status.svg
[dev-deps-url]: https://david-dm.org/ljharb/shell-quote#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/shell-quote.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/shell-quote.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/shell-quote.svg
[downloads-url]: https://npm-stat.com/charts.html?package=shell-quote
[codecov-image]: https://codecov.io/gh/ljharb/shell-quote/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/ljharb/shell-quote/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/shell-quote
[actions-url]: https://github.com/ljharb/shell-quote/actions


code:
'use strict';

var parse = require('../').parse;
var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' });
console.dir(xs);


code:
'use strict';

var parse = require('../').parse;
var xs = parse('beep || boop > /byte');
console.dir(xs);


code:
'use strict';

var parse = require('../').parse;
var xs = parse('a "b c" \\$def \'it\\\'s great\'');
console.dir(xs);


code:
'use strict';

var quote = require('../').quote;
var s = quote(['a', 'b c d', '$f', '"g"']);
console.log(s);


code:
'use strict';

exports.quote = require('./quote');
exports.parse = require('./parse');


shell-quote

description: quote and parse shell commands

keywords: command parse quote shell

output: CHANGELOG.md


code:
'use strict';

// '<(' is process substitution operator and
// can be parsed the same as control operator
var CONTROL = '(?:' + [
	'\\|\\|',
	'\\&\\&',
	';;',
	'\\|\\&',
	'\\<\\(',
	'\\<\\<\\<',
	'>>',
	'>\\&',
	'<\\&',
	'[&;()|<>]'
].join('|') + ')';
var controlRE = new RegExp('^' + CONTROL + '$');
var META = '|&;()<> \\t';
var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"';
var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\'';
var hash = /^#$/;

var SQ = "'";
var DQ = '"';
var DS = '$';

var TOKEN = '';
var mult = 0x100000000; // Math.pow(16, 8);
for (var i = 0; i < 4; i++) {
	TOKEN += (mult * Math.random()).toString(16);
}
var startsWithToken = new RegExp('^' + TOKEN);

function matchAll(s, r) {
	var origIndex = r.lastIndex;

	var matches = [];
	var matchObj;

	while ((matchObj = r.exec(s))) {
		matches.push(matchObj);
		if (r.lastIndex === matchObj.index) {
			r.lastIndex += 1;
		}
	}

	r.lastIndex = origIndex;

	return matches;
}

function getVar(env, pre, key) {
	var r = typeof env === 'function' ? env(key) : env[key];
	if (typeof r === 'undefined' && key != '') {
		r = '';
	} else if (typeof r === 'undefined') {
		r = '$';
	}

	if (typeof r === 'object') {
		return pre + TOKEN + JSON.stringify(r) + TOKEN;
	}
	return pre + r;
}

function parseInternal(string, env, opts) {
	if (!opts) {
		opts = {};
	}
	var BS = opts.escape || '\\';
	var BAREWORD = '(\\' + BS + '[\'"' + META + ']|[^\\s\'"' + META + '])+';

	var chunker = new RegExp([
		'(' + CONTROL + ')', // control chars
		'(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')+'
	].join('|'), 'g');

	var matches = matchAll(string, chunker);

	if (matches.length === 0) {
		return [];
	}
	if (!env) {
		env = {};
	}

	var commented = false;

	return matches.map(function (match) {
		var s = match[0];
		if (!s || commented) {
			return void undefined;
		}
		if (controlRE.test(s)) {
			return { op: s };
		}

		// Hand-written scanner/parser for Bash quoting rules:
		//
		// 1. inside single quotes, all characters are printed literally.
		// 2. inside double quotes, all characters are printed literally
		//    except variables prefixed by '$' and backslashes followed by
		//    either a double quote or another backslash.
		// 3. outside of any quotes, backslashes are treated as escape
		//    characters and not printed (unless they are themselves escaped)
		// 4. quote context can switch mid-token if there is no whitespace
		//     between the two quote contexts (e.g. all'one'"token" parses as
		//     "allonetoken")
		var quote = false;
		var esc = false;
		var out = '';
		var isGlob = false;
		var i;

		function parseEnvVar() {
			i += 1;
			var varend;
			var varname;
			var char = s.charAt(i);

			if (char === '{') {
				i += 1;
				if (s.charAt(i) === '}') {
					throw new Error('Bad substitution: ' + s.slice(i - 2, i + 1));
				}
				varend = s.indexOf('}', i);
				if (varend < 0) {
					throw new Error('Bad substitution: ' + s.slice(i));
				}
				varname = s.slice(i, varend);
				i = varend;
			} else if ((/[*@#?$!_-]/).test(char)) {
				varname = char;
				i += 1;
			} else {
				var slicedFromI = s.slice(i);
				varend = slicedFromI.match(/[^\w\d_]/);
				if (!varend) {
					varname = slicedFromI;
					i = s.length;
				} else {
					varname = slicedFromI.slice(0, varend.index);
					i += varend.index - 1;
				}
			}
			return getVar(env, '', varname);
		}

		for (i = 0; i < s.length; i++) {
			var c = s.charAt(i);
			isGlob = isGlob || (!quote && (c === '*' || c === '?'));
			if (esc) {
				out += c;
				esc = false;
			} else if (quote) {
				if (c === quote) {
					quote = false;
				} else if (quote == SQ) {
					out += c;
				} else { // Double quote
					if (c === BS) {
						i += 1;
						c = s.charAt(i);
						if (c === DQ || c === BS || c === DS) {
							out += c;
						} else {
							out += BS + c;
						}
					} else if (c === DS) {
						out += parseEnvVar();
					} else {
						out += c;
					}
				}
			} else if (c === DQ || c === SQ) {
				quote = c;
			} else if (controlRE.test(c)) {
				return { op: s };
			} else if (hash.test(c)) {
				commented = true;
				var commentObj = { comment: string.slice(match.index + i + 1) };
				if (out.length) {
					return [out, commentObj];
				}
				return [commentObj];
			} else if (c === BS) {
				esc = true;
			} else if (c === DS) {
				out += parseEnvVar();
			} else {
				out += c;
			}
		}

		if (isGlob) {
			return { op: 'glob', pattern: out };
		}

		return out;
	}).reduce(function (prev, arg) { // finalize parsed arguments
		// TODO: replace this whole reduce with a concat
		return typeof arg === 'undefined' ? prev : prev.concat(arg);
	}, []);
}

module.exports = function parse(s, env, opts) {
	var mapped = parseInternal(s, env, opts);
	if (typeof env !== 'function') {
		return mapped;
	}
	return mapped.reduce(function (acc, s) {
		if (typeof s === 'object') {
			return acc.concat(s);
		}
		var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g'));
		if (xs.length === 1) {
			return acc.concat(xs[0]);
		}
		return acc.concat(xs.filter(Boolean).map(function (x) {
			if (startsWithToken.test(x)) {
				return JSON.parse(x.split(TOKEN)[1]);
			}
			return x;
		}));
	}, []);
};


code:
# Security Policy

## Supported Versions

Only the latest major version is supported at any given time.

## Reporting a Vulnerability

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

test('comment', function (t) {
	t.same(parse('beep#boop'), ['beep', { comment: 'boop' }]);
	t.same(parse('beep #boop'), ['beep', { comment: 'boop' }]);
	t.same(parse('beep # boop'), ['beep', { comment: ' boop' }]);
	t.same(parse('beep # > boop'), ['beep', { comment: ' > boop' }]);
	t.same(parse('beep # "> boop"'), ['beep', { comment: ' "> boop"' }]);
	t.same(parse('beep "#"'), ['beep', '#']);
	t.same(parse('beep #"#"#'), ['beep', { comment: '"#"#' }]);
	t.same(parse('beep > boop # > foo'), ['beep', { op: '>' }, 'boop', { comment: ' > foo' }]);
	t.end();
});


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

test('expand environment variables', function (t) {
	t.same(parse('a $XYZ c', { XYZ: 'b' }), ['a', 'b', 'c']);
	t.same(parse('a${XYZ}c', { XYZ: 'b' }), ['abc']);
	t.same(parse('a${XYZ}c $XYZ', { XYZ: 'b' }), ['abc', 'b']);
	t.same(parse('"-$X-$Y-"', { X: 'a', Y: 'b' }), ['-a-b-']);
	t.same(parse("'-$X-$Y-'", { X: 'a', Y: 'b' }), ['-$X-$Y-']);
	t.same(parse('qrs"$zzz"wxy', { zzz: 'tuv' }), ['qrstuvwxy']);
	t.same(parse("qrs'$zzz'wxy", { zzz: 'tuv' }), ['qrs$zzzwxy']);
	t.same(parse('qrs${zzz}wxy'), ['qrswxy']);
	t.same(parse('qrs$wxy $'), ['qrs', '$']);
	t.same(parse('grep "xy$"'), ['grep', 'xy$']);
	t.same(parse('ab$x', { x: 'c' }), ['abc']);
	t.same(parse('ab\\$x', { x: 'c' }), ['ab$x']);
	t.same(parse('ab${x}def', { x: 'c' }), ['abcdef']);
	t.same(parse('ab\\${x}def', { x: 'c' }), ['ab${x}def']);
	t.same(parse('"ab\\${x}def"', { x: 'c' }), ['ab${x}def']);

	t.end();
});

test('expand environment variables within here-strings', function (t) {
	t.same(parse('a <<< $x', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
	t.same(parse('a <<< ${x}', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
	t.same(parse('a <<< "$x"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
	t.same(parse('a <<< "${x}"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);

	t.end();
});

test('environment variables with metacharacters', function (t) {
	t.same(parse('a $XYZ c', { XYZ: '"b"' }), ['a', '"b"', 'c']);
	t.same(parse('a $XYZ c', { XYZ: '$X', X: 5 }), ['a', '$X', 'c']);
	t.same(parse('a"$XYZ"c', { XYZ: "'xyz'" }), ["a'xyz'c"]);

	t.end();
});

test('special shell parameters', function (t) {
	var chars = '*@#?-$!0_'.split('');
	t.plan(chars.length);

	chars.forEach(function (c) {
		var env = {};
		env[c] = 'xxx';
		t.same(parse('a $' + c + ' c', env), ['a', 'xxx', 'c']);
	});
});


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

function getEnv() {
	return 'xxx';
}

function getEnvObj() {
	return { op: '@@' };
}

test('functional env expansion', function (t) {
	t.plan(4);

	t.same(parse('a $XYZ c', getEnv), ['a', 'xxx', 'c']);
	t.same(parse('a $XYZ c', getEnvObj), ['a', { op: '@@' }, 'c']);
	t.same(parse('a${XYZ}c', getEnvObj), ['a', { op: '@@' }, 'c']);
	t.same(parse('"a $XYZ c"', getEnvObj), ['a ', { op: '@@' }, ' c']);
});


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

test('single operators', function (t) {
	t.same(parse('beep | boop'), ['beep', { op: '|' }, 'boop']);
	t.same(parse('beep|boop'), ['beep', { op: '|' }, 'boop']);
	t.same(parse('beep \\| boop'), ['beep', '|', 'boop']);
	t.same(parse('beep "|boop"'), ['beep', '|boop']);

	t.same(parse('echo zing &'), ['echo', 'zing', { op: '&' }]);
	t.same(parse('echo zing&'), ['echo', 'zing', { op: '&' }]);
	t.same(parse('echo zing\\&'), ['echo', 'zing&']);
	t.same(parse('echo "zing\\&"'), ['echo', 'zing\\&']);

	t.same(parse('beep;boop'), ['beep', { op: ';' }, 'boop']);
	t.same(parse('(beep;boop)'), [
		{ op: '(' }, 'beep', { op: ';' }, 'boop', { op: ')' }
	]);

	t.same(parse('beep>boop'), ['beep', { op: '>' }, 'boop']);
	t.same(parse('beep 2>boop'), ['beep', '2', { op: '>' }, 'boop']);
	t.same(parse('beep<boop'), ['beep', { op: '<' }, 'boop']);

	t.end();
});

test('double operators', function (t) {
	t.same(parse('beep || boop'), ['beep', { op: '||' }, 'boop']);
	t.same(parse('beep||boop'), ['beep', { op: '||' }, 'boop']);
	t.same(parse('beep ||boop'), ['beep', { op: '||' }, 'boop']);
	t.same(parse('beep|| boop'), ['beep', { op: '||' }, 'boop']);
	t.same(parse('beep  ||   boop'), ['beep', { op: '||' }, 'boop']);

	t.same(parse('beep && boop'), ['beep', { op: '&&' }, 'boop']);
	t.same(
		parse('beep && boop || byte'),
		['beep', { op: '&&' }, 'boop', { op: '||' }, 'byte']
	);
	t.same(
		parse('beep&&boop||byte'),
		['beep', { op: '&&' }, 'boop', { op: '||' }, 'byte']
	);
	t.same(
		parse('beep\\&\\&boop||byte'),
		['beep&&boop', { op: '||' }, 'byte']
	);
	t.same(
		parse('beep\\&&boop||byte'),
		['beep&', { op: '&' }, 'boop', { op: '||' }, 'byte']
	);
	t.same(
		parse('beep;;boop|&byte>>blip'),
		['beep', { op: ';;' }, 'boop', { op: '|&' }, 'byte', { op: '>>' }, 'blip']
	);

	t.same(parse('beep 2>&1'), ['beep', '2', { op: '>&' }, '1']);

	t.same(
		parse('beep<(boop)'),
		['beep', { op: '<(' }, 'boop', { op: ')' }]
	);
	t.same(
		parse('beep<<(boop)'),
		['beep', { op: '<' }, { op: '<(' }, 'boop', { op: ')' }]
	);

	t.end();
});

test('duplicating input file descriptors', function (t) {
	// duplicating stdout to file descriptor 3
	t.same(parse('beep 3<&1'), ['beep', '3', { op: '<&' }, '1']);

	// duplicating stdout to file descriptor 0, i.e. stdin
	t.same(parse('beep <&1'), ['beep', { op: '<&' }, '1']);

	// closes stdin
	t.same(parse('beep <&-'), ['beep', { op: '<&' }, '-']);

	t.end();
});

test('here strings', function (t) {
	t.same(parse('cat <<< "hello world"'), ['cat', { op: '<<<' }, 'hello world']);
	t.same(parse('cat <<< hello'), ['cat', { op: '<<<' }, 'hello']);
	t.same(parse('cat<<<hello'), ['cat', { op: '<<<' }, 'hello']);
	t.same(parse('cat<<<"hello world"'), ['cat', { op: '<<<' }, 'hello world']);

	t.end();
});

test('glob patterns', function (t) {
	t.same(
		parse('tap test/*.test.js'),
		['tap', { op: 'glob', pattern: 'test/*.test.js' }]
	);

	t.same(parse('tap "test/*.test.js"'), ['tap', 'test/*.test.js']);
	t.end();
});


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

test('parse shell commands', function (t) {
	t.same(parse(''), [], 'parses an empty string');

	t['throws'](
		function () { parse('${}'); },
		Error,
		'empty substitution throws'
	);
	t['throws'](
		function () { parse('${'); },
		Error,
		'incomplete substitution throws'
	);

	t.same(parse('a \'b\' "c"'), ['a', 'b', 'c']);
	t.same(
		parse('beep "boop" \'foo bar baz\' "it\'s \\"so\\" groovy"'),
		['beep', 'boop', 'foo bar baz', 'it\'s "so" groovy']
	);
	t.same(parse('a b\\ c d'), ['a', 'b c', 'd']);
	t.same(parse('\\$beep bo\\`op'), ['$beep', 'bo`op']);
	t.same(parse('echo "foo = \\"foo\\""'), ['echo', 'foo = "foo"']);
	t.same(parse(''), []);
	t.same(parse(' '), []);
	t.same(parse('\t'), []);
	t.same(parse('a"b c d"e'), ['ab c de']);
	t.same(parse('a\\ b"c d"\\ e f'), ['a bc d e', 'f']);
	t.same(parse('a\\ b"c d"\\ e\'f g\' h'), ['a bc d ef g', 'h']);
	t.same(parse("x \"bl'a\"'h'"), ['x', "bl'ah"]);
	t.same(parse("x bl^'a^'h'", {}, { escape: '^' }), ['x', "bl'a'h"]);
	t.same(parse('abcH def', {}, { escape: 'H' }), ['abc def']);

	t.deepEqual(parse('# abc  def  ghi'), [{ comment: ' abc  def  ghi' }], 'start-of-line comment content is unparsed');
	t.deepEqual(parse('xyz # abc  def  ghi'), ['xyz', { comment: ' abc  def  ghi' }], 'comment content is unparsed');

	t.end();
});


code:
'use strict';

var test = require('tape');
var quote = require('../').quote;

test('quote', function (t) {
	t.equal(quote(['a', 'b', 'c d']), 'a b \'c d\'');
	t.equal(
		quote(['a', 'b', "it's a \"neat thing\""]),
		'a b "it\'s a \\"neat thing\\""'
	);
	t.equal(
		quote(['$', '`', '\'']),
		'\\$ \\` "\'"'
	);
	t.equal(quote([]), '');
	t.equal(quote(['a\nb']), "'a\nb'");
	t.equal(quote([' #(){}*|][!']), "' #(){}*|][!'");
	t.equal(quote(["'#(){}*|][!"]), '"\'#(){}*|][\\!"');
	t.equal(quote(['X#(){}*|][!']), 'X\\#\\(\\)\\{\\}\\*\\|\\]\\[\\!');
	t.equal(quote(['a\n#\nb']), "'a\n#\nb'");
	t.equal(quote(['><;{}']), '\\>\\<\\;\\{\\}');
	t.equal(quote(['a', 1, true, false]), 'a 1 true false');
	t.equal(quote(['a', 1, null, undefined]), 'a 1 null undefined');
	t.equal(quote(['a\\x']), 'a\\\\x');
	t.end();
});

test('quote ops', function (t) {
	t.equal(quote(['a', { op: '|' }, 'b']), 'a \\| b');
	t.equal(
		quote(['a', { op: '&&' }, 'b', { op: ';' }, 'c']),
		'a \\&\\& b \\; c'
	);
	t.end();
});

test('quote windows paths', { skip: 'breaking change, disabled until 2.x' }, function (t) {
	var path = 'C:\\projects\\node-shell-quote\\index.js';

	t.equal(quote([path, 'b', 'c d']), 'C:\\projects\\node-shell-quote\\index.js b \'c d\'');

	t.end();
});

test("chars for windows paths don't break out", function (t) {
	var x = '`:\\a\\b';
	t.equal(quote([x]), '\\`\\:\\\\a\\\\b');
	t.end();
});


code:
'use strict';

var test = require('tape');
var parse = require('../').parse;

test('set env vars', function (t) {
	t.same(
		parse('ABC=444 x y z'),
		['ABC=444', 'x', 'y', 'z']
	);
	t.same(
		parse('ABC=3\\ 4\\ 5 x y z'),
		['ABC=3 4 5', 'x', 'y', 'z']
	);
	t.same(
		parse('X="7 8 9" printx'),
		['X=7 8 9', 'printx']
	);
	t.same(
		parse('X="7 8 9"; printx'),
		['X=7 8 9', { op: ';' }, 'printx']
	);
	t.same(
		parse('X="7 8 9"; printx', function () {
			t.fail('should not have matched any keys');
		}),
		['X=7 8 9', { op: ';' }, 'printx']
	);

	t.end();
});


code:
root = true

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


code:
{
	"root": true,

	"extends": "@ljharb",

	"rules": {
		"max-lines-per-function": 0,
		"multiline-comment-style": 1,
		"new-cap": [2, { "capIsNewExceptions": ["GetIntrinsic"] }],
	},
}


code:
# These are supported funding model platforms

github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/side-channel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


code:
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.0.6](https://github.com/ljharb/side-channel/compare/v1.0.5...v1.0.6) - 2024-02-29

### Commits

- add types [`9beef66`](https://github.com/ljharb/side-channel/commit/9beef6643e6d717ea57bedabf86448123a7dd9e9)
- [meta] simplify `exports` [`4334cf9`](https://github.com/ljharb/side-channel/commit/4334cf9df654151504c383b62a2f9ebdc8d9d5ac)
- [Deps] update `call-bind` [`d6043c4`](https://github.com/ljharb/side-channel/commit/d6043c4d8f4d7be9037dd0f0419c7a2e0e39ec6a)
- [Dev Deps] update `tape` [`6aca376`](https://github.com/ljharb/side-channel/commit/6aca3761868dc8cd5ff7fd9799bf6b95e09a6eb0)

## [v1.0.5](https://github.com/ljharb/side-channel/compare/v1.0.4...v1.0.5) - 2024-02-06

### Commits

- [actions] reuse common workflows [`3d2e1ff`](https://github.com/ljharb/side-channel/commit/3d2e1ffd16dd6eaaf3e40ff57951f840d2d63c04)
- [meta] use `npmignore` to autogenerate an npmignore file [`04296ea`](https://github.com/ljharb/side-channel/commit/04296ea17d1544b0a5d20fd5bfb31aa4f6513eb9)
- [meta] add `.editorconfig`; add `eclint` [`130f0a6`](https://github.com/ljharb/side-channel/commit/130f0a6adbc04d385c7456a601d38344dce3d6a9)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `safe-publish-latest`, `tape` [`d480c2f`](https://github.com/ljharb/side-channel/commit/d480c2fbe757489ae9b4275491ffbcc3ac4725e9)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `tape` [`ecbe70e`](https://github.com/ljharb/side-channel/commit/ecbe70e53a418234081a77971fec1fdfae20c841)
- [actions] update rebase action [`75240b9`](https://github.com/ljharb/side-channel/commit/75240b9963b816e8846400d2287cb68f88c7fba7)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `npmignore`, `tape` [`ae8d281`](https://github.com/ljharb/side-channel/commit/ae8d281572430099109870fd9430d2ca3f320b8d)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`7125b88`](https://github.com/ljharb/side-channel/commit/7125b885fd0eacad4fee9b073b72d14065ece278)
- [Deps] update `call-bind`, `get-intrinsic`, `object-inspect` [`82577c9`](https://github.com/ljharb/side-channel/commit/82577c9796304519139a570f82a317211b5f3b86)
- [Deps] update `call-bind`, `get-intrinsic`, `object-inspect` [`550aadf`](https://github.com/ljharb/side-channel/commit/550aadf20475a6081fd70304cc54f77259a5c8a8)
- [Tests] increase coverage [`5130877`](https://github.com/ljharb/side-channel/commit/5130877a7b27c862e64e6d1c12a178b28808859d)
- [Deps] update `get-intrinsic`, `object-inspect` [`ba0194c`](https://github.com/ljharb/side-channel/commit/ba0194c505b1a8a0427be14cadd5b8a46d4d01b8)
- [meta] add missing `engines.node` [`985fd24`](https://github.com/ljharb/side-channel/commit/985fd249663cb06617a693a94fe08cad12f5cb70)
- [Refactor] use `es-errors`, so things that only need those do not need `get-intrinsic` [`40227a8`](https://github.com/ljharb/side-channel/commit/40227a87b01709ad2c0eebf87eb4223a800099b9)
- [Deps] update `get-intrinsic` [`a989b40`](https://github.com/ljharb/side-channel/commit/a989b4024958737ae7be9fbffdeff2078f33a0fd)
- [Deps] update `object-inspect` [`aec42d2`](https://github.com/ljharb/side-channel/commit/aec42d2ec541a31aaa02475692c87d489237d9a3)

## [v1.0.4](https://github.com/ljharb/side-channel/compare/v1.0.3...v1.0.4) - 2020-12-29

### Commits

- [Tests] migrate tests to Github Actions [`10909cb`](https://github.com/ljharb/side-channel/commit/10909cbf8ce9c0bf96f604cf13d7ffd5a22c2d40)
- [Refactor] Use a linked list rather than an array, and move accessed nodes to the beginning [`195613f`](https://github.com/ljharb/side-channel/commit/195613f28b5c1e6072ef0b61b5beebaf2b6a304e)
- [meta] do not publish github action workflow files [`290ec29`](https://github.com/ljharb/side-channel/commit/290ec29cd21a60585145b4a7237ec55228c52c27)
- [Tests] run `nyc` on all tests; use `tape` runner [`ea6d030`](https://github.com/ljharb/side-channel/commit/ea6d030ff3fe6be2eca39e859d644c51ecd88869)
- [actions] add "Allow Edits" workflow [`d464d8f`](https://github.com/ljharb/side-channel/commit/d464d8fe52b5eddf1504a0ed97f0941a90f32c15)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog` [`02daca8`](https://github.com/ljharb/side-channel/commit/02daca87c6809821c97be468d1afa2f5ef447383)
- [Refactor] use `call-bind` and `get-intrinsic` instead of `es-abstract` [`e09d481`](https://github.com/ljharb/side-channel/commit/e09d481528452ebafa5cdeae1af665c35aa2deee)
- [Deps] update `object.assign` [`ee83aa8`](https://github.com/ljharb/side-channel/commit/ee83aa81df313b5e46319a63adb05cf0c179079a)
- [actions] update rebase action to use checkout v2 [`7726b0b`](https://github.com/ljharb/side-channel/commit/7726b0b058b632fccea709f58960871defaaa9d7)

## [v1.0.3](https://github.com/ljharb/side-channel/compare/v1.0.2...v1.0.3) - 2020-08-23

### Commits

- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `tape` [`1f10561`](https://github.com/ljharb/side-channel/commit/1f105611ef3acf32dec8032ae5c0baa5e56bb868)
- [Deps] update `es-abstract`, `object-inspect` [`bc20159`](https://github.com/ljharb/side-channel/commit/bc201597949a505e37cef9eaf24c7010831e6f03)
- [Dev Deps] update `@ljharb/eslint-config`, `tape` [`b9b2b22`](https://github.com/ljharb/side-channel/commit/b9b2b225f9e0ea72a6ec2b89348f0bd690bc9ed1)
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape` [`7055ab4`](https://github.com/ljharb/side-channel/commit/7055ab4de0860606efd2003674a74f1fe6ebc07e)
- [Dev Deps] update `auto-changelog`; add `aud` [`d278c37`](https://github.com/ljharb/side-channel/commit/d278c37d08227be4f84aa769fcd919e73feeba40)
- [actions] switch Automatic Rebase workflow to `pull_request_target` event [`3bcf982`](https://github.com/ljharb/side-channel/commit/3bcf982faa122745b39c33ce83d32fdf003741c6)
- [Tests] only audit prod deps [`18d01c4`](https://github.com/ljharb/side-channel/commit/18d01c4015b82a3d75044c4d5ba7917b2eac01ec)
- [Deps] update `es-abstract` [`6ab096d`](https://github.com/ljharb/side-channel/commit/6ab096d9de2b482cf5e0717e34e212f5b2b9bc9a)
- [Dev Deps] update `tape` [`9dc174c`](https://github.com/ljharb/side-channel/commit/9dc174cc651dfd300b4b72da936a0a7eda5f9452)
- [Deps] update `es-abstract` [`431d0f0`](https://github.com/ljharb/side-channel/commit/431d0f0ff11fbd2ae6f3115582a356d3a1cfce82)
- [Deps] update `es-abstract` [`49869fd`](https://github.com/ljharb/side-channel/commit/49869fd323bf4453f0ba515c0fb265cf5ab7b932)
- [meta] Add package.json to package's exports [`77d9cdc`](https://github.com/ljharb/side-channel/commit/77d9cdceb2a9e47700074f2ae0c0a202e7dac0d4)

## [v1.0.2](https://github.com/ljharb/side-channel/compare/v1.0.1...v1.0.2) - 2019-12-20

### Commits

- [Dev Deps] update `@ljharb/eslint-config`, `tape` [`4a526df`](https://github.com/ljharb/side-channel/commit/4a526df44e4701566ed001ec78546193f818b082)
- [Deps] update `es-abstract` [`d4f6e62`](https://github.com/ljharb/side-channel/commit/d4f6e629b6fb93a07415db7f30d3c90fd7f264fe)

## [v1.0.1](https://github.com/ljharb/side-channel/compare/v1.0.0...v1.0.1) - 2019-12-01

### Commits

- [Fix] add missing "exports" [`d212907`](https://github.com/ljharb/side-channel/commit/d2129073abf0701a5343bf28aa2145617604dc2e)

## v1.0.0 - 2019-12-01

### Commits

- Initial implementation [`dbebd3a`](https://github.com/ljharb/side-channel/commit/dbebd3a4b5ed64242f9a6810efe7c4214cd8cde4)
- Initial tests [`73bdefe`](https://github.com/ljharb/side-channel/commit/73bdefe568c9076cf8c0b8719bc2141aec0e19b8)
- Initial commit [`43c03e1`](https://github.com/ljharb/side-channel/commit/43c03e1c2849ec50a87b7a5cd76238a62b0b8770)
- npm init [`5c090a7`](https://github.com/ljharb/side-channel/commit/5c090a765d66a5527d9889b89aeff78dee91348c)
- [meta] add `auto-changelog` [`a5c4e56`](https://github.com/ljharb/side-channel/commit/a5c4e5675ec02d5eb4d84b4243aeea2a1d38fbec)
- [actions] add automatic rebasing / merge commit blocking [`bab1683`](https://github.com/ljharb/side-channel/commit/bab1683d8f9754b086e94397699fdc645e0d7077)
- [meta] add `funding` field; create FUNDING.yml [`63d7aea`](https://github.com/ljharb/side-channel/commit/63d7aeaf34f5650650ae97ca4b9fae685bd0937c)
- [Tests] add `npm run lint` [`46a5a81`](https://github.com/ljharb/side-channel/commit/46a5a81705cd2664f83df232c01dbbf2ee952885)
- Only apps should have lockfiles [`8b16b03`](https://github.com/ljharb/side-channel/commit/8b16b0305f00895d90c4e2e5773c854cfea0e448)
- [meta] add `safe-publish-latest` [`2f098ef`](https://github.com/ljharb/side-channel/commit/2f098ef092a39399cfe548b19a1fc03c2fd2f490)


code:
# side-channel
Store information about any JS value in a side channel. Uses WeakMap if available.


code:
declare namespace getSideChannel {
	type Key = unknown;
	type ListNode<T> = {
		key: Key;
		next: ListNode<T>;
		value: T;
	};
	type RootNode<T> = {
		key: object;
		next: null | ListNode<T>;
	};
	function listGetNode<T>(list: RootNode<T>, key: ListNode<T>['key']): ListNode<T> | void;
	function listGet<T>(objects: RootNode<T>, key: ListNode<T>['key']): T | void;
	function listSet<T>(objects: RootNode<T>, key: ListNode<T>['key'], value: T): void;
	function listHas<T>(objects: RootNode<T>, key: ListNode<T>['key']): boolean;

	type Channel = {
		assert: (key: Key) => void;
		has: (key: Key) => boolean;
		get: <T>(key: Key) => T;
		set: <T>(key: Key, value: T) => void;
	}
}

declare function getSideChannel(): getSideChannel.Channel;

export = getSideChannel;