This commit is contained in:
2025-05-09 05:30:08 +02:00
parent 7bb10e7df4
commit 73367bad9e
5322 changed files with 1266973 additions and 313 deletions

View File

@@ -0,0 +1,664 @@
semver(1) -- The semantic versioner for npm
===========================================
## Install
```bash
npm install semver
````
## Usage
As a node module:
```js
const semver = require('semver')
semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true
semver.minVersion('>=1.0.0') // '1.0.0'
semver.valid(semver.coerce('v2')) // '2.0.0'
semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7'
```
You can also just load the module for the function that you care about if
you'd like to minimize your footprint.
```js
// load the whole API at once in a single object
const semver = require('semver')
// or just load the bits you need
// all of them listed here, just pick and choose what you want
// classes
const SemVer = require('semver/classes/semver')
const Comparator = require('semver/classes/comparator')
const Range = require('semver/classes/range')
// functions for working with versions
const semverParse = require('semver/functions/parse')
const semverValid = require('semver/functions/valid')
const semverClean = require('semver/functions/clean')
const semverInc = require('semver/functions/inc')
const semverDiff = require('semver/functions/diff')
const semverMajor = require('semver/functions/major')
const semverMinor = require('semver/functions/minor')
const semverPatch = require('semver/functions/patch')
const semverPrerelease = require('semver/functions/prerelease')
const semverCompare = require('semver/functions/compare')
const semverRcompare = require('semver/functions/rcompare')
const semverCompareLoose = require('semver/functions/compare-loose')
const semverCompareBuild = require('semver/functions/compare-build')
const semverSort = require('semver/functions/sort')
const semverRsort = require('semver/functions/rsort')
// low-level comparators between versions
const semverGt = require('semver/functions/gt')
const semverLt = require('semver/functions/lt')
const semverEq = require('semver/functions/eq')
const semverNeq = require('semver/functions/neq')
const semverGte = require('semver/functions/gte')
const semverLte = require('semver/functions/lte')
const semverCmp = require('semver/functions/cmp')
const semverCoerce = require('semver/functions/coerce')
// working with ranges
const semverSatisfies = require('semver/functions/satisfies')
const semverMaxSatisfying = require('semver/ranges/max-satisfying')
const semverMinSatisfying = require('semver/ranges/min-satisfying')
const semverToComparators = require('semver/ranges/to-comparators')
const semverMinVersion = require('semver/ranges/min-version')
const semverValidRange = require('semver/ranges/valid')
const semverOutside = require('semver/ranges/outside')
const semverGtr = require('semver/ranges/gtr')
const semverLtr = require('semver/ranges/ltr')
const semverIntersects = require('semver/ranges/intersects')
const semverSimplifyRange = require('semver/ranges/simplify')
const semverRangeSubset = require('semver/ranges/subset')
```
As a command-line utility:
```
$ semver -h
A JavaScript implementation of the https://semver.org/ specification
Copyright Isaac Z. Schlueter
Usage: semver [options] <version> [<version> [...]]
Prints valid versions sorted by SemVer precedence
Options:
-r --range <range>
Print versions that match the specified range.
-i --increment [<level>]
Increment a version by the specified level. Level can
be one of: major, minor, patch, premajor, preminor,
prepatch, prerelease, or release. Default level is 'patch'.
Only one version may be specified.
--preid <identifier>
Identifier to be used to prefix premajor, preminor,
prepatch or prerelease version increments.
-l --loose
Interpret versions and ranges loosely
-n <0|1>
This is the base to be used for the prerelease identifier.
-p --include-prerelease
Always include prerelease versions in range matching
-c --coerce
Coerce a string into SemVer if possible
(does not imply --loose)
--rtl
Coerce version strings right to left
--ltr
Coerce version strings left to right (default)
Program exits successfully if any valid version satisfies
all supplied ranges, and prints all satisfying versions.
If no satisfying versions are found, then exits failure.
Versions are printed in ascending order, so supplying
multiple versions to the utility will just sort them.
```
## Versions
A "version" is described by the `v2.0.0` specification found at
<https://semver.org/>.
A leading `"="` or `"v"` character is stripped off and ignored.
Support for stripping a leading "v" is kept for compatibility with `v1.0.0` of the SemVer
specification but should not be used anymore.
## Ranges
A `version range` is a set of `comparators` that specify versions
that satisfy the range.
A `comparator` is composed of an `operator` and a `version`. The set
of primitive `operators` is:
* `<` Less than
* `<=` Less than or equal to
* `>` Greater than
* `>=` Greater than or equal to
* `=` Equal. If no operator is specified, then equality is assumed,
so this operator is optional but MAY be included.
For example, the comparator `>=1.2.7` would match the versions
`1.2.7`, `1.2.8`, `2.5.3`, and `1.3.9`, but not the versions `1.2.6`
or `1.1.0`. The comparator `>1` is equivalent to `>=2.0.0` and
would match the versions `2.0.0` and `3.1.0`, but not the versions
`1.0.1` or `1.1.0`.
Comparators can be joined by whitespace to form a `comparator set`,
which is satisfied by the **intersection** of all of the comparators
it includes.
A range is composed of one or more comparator sets, joined by `||`. A
version matches a range if and only if every comparator in at least
one of the `||`-separated comparator sets is satisfied by the version.
For example, the range `>=1.2.7 <1.3.0` would match the versions
`1.2.7`, `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`,
or `1.1.0`.
The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`,
`1.2.9`, and `1.4.6`, but not the versions `1.2.8` or `2.0.0`.
### Prerelease Tags
If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then
it will only be allowed to satisfy comparator sets if at least one
comparator with the same `[major, minor, patch]` tuple also has a
prerelease tag.
For example, the range `>1.2.3-alpha.3` would be allowed to match the
version `1.2.3-alpha.7`, but it would *not* be satisfied by
`3.4.5-alpha.9`, even though `3.4.5-alpha.9` is technically "greater
than" `1.2.3-alpha.3` according to the SemVer sort rules. The version
range only accepts prerelease tags on the `1.2.3` version.
Version `3.4.5` *would* satisfy the range because it does not have a
prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`.
The purpose of this behavior is twofold. First, prerelease versions
frequently are updated very quickly, and contain many breaking changes
that are (by the author's design) not yet fit for public consumption.
Therefore, by default, they are excluded from range-matching
semantics.
Second, a user who has opted into using a prerelease version has
indicated the intent to use *that specific* set of
alpha/beta/rc versions. By including a prerelease tag in the range,
the user is indicating that they are aware of the risk. However, it
is still not appropriate to assume that they have opted into taking a
similar risk on the *next* set of prerelease versions.
Note that this behavior can be suppressed (treating all prerelease
versions as if they were normal versions, for range-matching)
by setting the `includePrerelease` flag on the options
object to any
[functions](https://github.com/npm/node-semver#functions) that do
range matching.
#### Prerelease Identifiers
The method `.inc` takes an additional `identifier` string argument that
will append the value of the string as a prerelease identifier:
```javascript
semver.inc('1.2.3', 'prerelease', 'beta')
// '1.2.4-beta.0'
```
command-line example:
```bash
$ semver 1.2.3 -i prerelease --preid beta
1.2.4-beta.0
```
Which then can be used to increment further:
```bash
$ semver 1.2.4-beta.0 -i prerelease
1.2.4-beta.1
```
To get out of the prerelease phase, use the `release` option:
```bash
$ semver 1.2.4-beta.1 -i release
1.2.4
```
#### Prerelease Identifier Base
The method `.inc` takes an optional parameter 'identifierBase' string
that will let you let your prerelease number as zero-based or one-based.
Set to `false` to omit the prerelease number altogether.
If you do not specify this parameter, it will default to zero-based.
```javascript
semver.inc('1.2.3', 'prerelease', 'beta', '1')
// '1.2.4-beta.1'
```
```javascript
semver.inc('1.2.3', 'prerelease', 'beta', false)
// '1.2.4-beta'
```
command-line example:
```bash
$ semver 1.2.3 -i prerelease --preid beta -n 1
1.2.4-beta.1
```
```bash
$ semver 1.2.3 -i prerelease --preid beta -n false
1.2.4-beta
```
### Advanced Range Syntax
Advanced range syntax desugars to primitive comparators in
deterministic ways.
Advanced ranges may be combined in the same way as primitive
comparators using white space or `||`.
#### Hyphen Ranges `X.Y.Z - A.B.C`
Specifies an inclusive set.
* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`
If a partial version is provided as the first version in the inclusive
range, then the missing pieces are replaced with zeroes.
* `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4`
If a partial version is provided as the second version in the
inclusive range, then all versions that start with the supplied parts
of the tuple are accepted, but nothing that would be greater than the
provided tuple parts.
* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0-0`
* `1.2.3 - 2` := `>=1.2.3 <3.0.0-0`
#### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
Any of `X`, `x`, or `*` may be used to "stand in" for one of the
numeric values in the `[major, minor, patch]` tuple.
* `*` := `>=0.0.0` (Any non-prerelease version satisfies, unless
`includePrerelease` is specified, in which case any version at all
satisfies)
* `1.x` := `>=1.0.0 <2.0.0-0` (Matching major version)
* `1.2.x` := `>=1.2.0 <1.3.0-0` (Matching major and minor versions)
A partial version range is treated as an X-Range, so the special
character is in fact optional.
* `""` (empty string) := `*` := `>=0.0.0`
* `1` := `1.x.x` := `>=1.0.0 <2.0.0-0`
* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0-0`
#### Tilde Ranges `~1.2.3` `~1.2` `~1`
Allows patch-level changes if a minor version is specified on the
comparator. Allows minor-level changes if not.
* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0-0`
* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0-0` (Same as `1.2.x`)
* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0-0` (Same as `1.x`)
* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0-0`
* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0-0` (Same as `0.2.x`)
* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0-0` (Same as `0.x`)
* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0-0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
#### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4`
Allows changes that do not modify the left-most non-zero element in the
`[major, minor, patch]` tuple. In other words, this allows patch and
minor updates for versions `1.0.0` and above, patch updates for
versions `0.X >=0.1.0`, and *no* updates for versions `0.0.X`.
Many authors treat a `0.x` version as if the `x` were the major
"breaking-change" indicator.
Caret ranges are ideal when an author may make breaking changes
between `0.2.4` and `0.3.0` releases, which is a common practice.
However, it presumes that there will *not* be breaking changes between
`0.2.4` and `0.2.5`. It allows for changes that are presumed to be
additive (but non-breaking), according to commonly observed practices.
* `^1.2.3` := `>=1.2.3 <2.0.0-0`
* `^0.2.3` := `>=0.2.3 <0.3.0-0`
* `^0.0.3` := `>=0.0.3 <0.0.4-0`
* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0-0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4-0` Note that prereleases in the
`0.0.3` version *only* will be allowed, if they are greater than or
equal to `beta`. So, `0.0.3-pr.2` would be allowed.
When parsing caret ranges, a missing `patch` value desugars to the
number `0`, but will allow flexibility within that value, even if the
major and minor versions are both `0`.
* `^1.2.x` := `>=1.2.0 <2.0.0-0`
* `^0.0.x` := `>=0.0.0 <0.1.0-0`
* `^0.0` := `>=0.0.0 <0.1.0-0`
A missing `minor` and `patch` values will desugar to zero, but also
allow flexibility within those values, even if the major version is
zero.
* `^1.x` := `>=1.0.0 <2.0.0-0`
* `^0.x` := `>=0.0.0 <1.0.0-0`
### Range Grammar
Putting all this together, here is a Backus-Naur grammar for ranges,
for the benefit of parser authors:
```bnf
range-set ::= range ( logical-or range ) *
logical-or ::= ( ' ' ) * '||' ( ' ' ) *
range ::= hyphen | simple ( ' ' simple ) * | ''
hyphen ::= partial ' - ' partial
simple ::= primitive | partial | tilde | caret
primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
xr ::= 'x' | 'X' | '*' | nr
nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
tilde ::= '~' partial
caret ::= '^' partial
qualifier ::= ( '-' pre )? ( '+' build )?
pre ::= parts
build ::= parts
parts ::= part ( '.' part ) *
part ::= nr | [-0-9A-Za-z]+
```
## Functions
All methods and classes take a final `options` object argument. All
options in this object are `false` by default. The options supported
are:
- `loose`: Be more forgiving about not-quite-valid semver strings.
(Any resulting output will always be 100% strict compliant, of
course.) For backwards compatibility reasons, if the `options`
argument is a boolean value instead of an object, it is interpreted
to be the `loose` param.
- `includePrerelease`: Set to suppress the [default
behavior](https://github.com/npm/node-semver#prerelease-tags) of
excluding prerelease tagged versions from ranges unless they are
explicitly opted into.
Strict-mode Comparators and Ranges will be strict about the SemVer
strings that they parse.
* `valid(v)`: Return the parsed version, or null if it's not valid.
* `inc(v, releaseType, options, identifier, identifierBase)`:
Return the version incremented by the release
type (`major`, `premajor`, `minor`, `preminor`, `patch`,
`prepatch`, `prerelease`, or `release`), or null if it's not valid
* `premajor` in one call will bump the version up to the next major
version and down to a prerelease of that major version.
`preminor`, and `prepatch` work the same way.
* If called from a non-prerelease version, `prerelease` will work the
same as `prepatch`. It increments the patch version and then makes a
prerelease. If the input version is already a prerelease it simply
increments it.
* `release` will remove any prerelease part of the version.
* `identifier` can be used to prefix `premajor`, `preminor`,
`prepatch`, or `prerelease` version increments. `identifierBase`
is the base to be used for the `prerelease` identifier.
* `prerelease(v)`: Returns an array of prerelease components, or null
if none exist. Example: `prerelease('1.2.3-alpha.1') -> ['alpha', 1]`
* `major(v)`: Return the major version number.
* `minor(v)`: Return the minor version number.
* `patch(v)`: Return the patch version number.
* `intersects(r1, r2, loose)`: Return true if the two supplied ranges
or comparators intersect.
* `parse(v)`: Attempt to parse a string as a semantic version, returning either
a `SemVer` object or `null`.
### Comparison
* `gt(v1, v2)`: `v1 > v2`
* `gte(v1, v2)`: `v1 >= v2`
* `lt(v1, v2)`: `v1 < v2`
* `lte(v1, v2)`: `v1 <= v2`
* `eq(v1, v2)`: `v1 == v2` This is true if they're logically equivalent,
even if they're not the same string. You already know how to
compare strings.
* `neq(v1, v2)`: `v1 != v2` The opposite of `eq`.
* `cmp(v1, comparator, v2)`: Pass in a comparison string, and it'll call
the corresponding function above. `"==="` and `"!=="` do simple
string comparison, but are included for completeness. Throws if an
invalid comparison string is provided.
* `compare(v1, v2)`: Return `0` if `v1 == v2`, or `1` if `v1` is greater, or `-1` if
`v2` is greater. Sorts in ascending order if passed to `Array.sort()`.
* `rcompare(v1, v2)`: The reverse of `compare`. Sorts an array of versions
in descending order when passed to `Array.sort()`.
* `compareBuild(v1, v2)`: The same as `compare` but considers `build` when two versions
are equal. Sorts in ascending order if passed to `Array.sort()`.
* `compareLoose(v1, v2)`: Short for `compare(v1, v2, { loose: true })`.
* `diff(v1, v2)`: Returns the difference between two versions by the release type
(`major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, or `prerelease`),
or null if the versions are the same.
### Sorting
* `sort(versions)`: Returns a sorted array of versions based on the `compareBuild`
function.
* `rsort(versions)`: The reverse of `sort`. Returns an array of versions based on
the `compareBuild` function in descending order.
### Comparators
* `intersects(comparator)`: Return true if the comparators intersect
### Ranges
* `validRange(range)`: Return the valid range or null if it's not valid.
* `satisfies(version, range)`: Return true if the version satisfies the
range.
* `maxSatisfying(versions, range)`: Return the highest version in the list
that satisfies the range, or `null` if none of them do.
* `minSatisfying(versions, range)`: Return the lowest version in the list
that satisfies the range, or `null` if none of them do.
* `minVersion(range)`: Return the lowest version that can match
the given range.
* `gtr(version, range)`: Return `true` if the version is greater than all the
versions possible in the range.
* `ltr(version, range)`: Return `true` if the version is less than all the
versions possible in the range.
* `outside(version, range, hilo)`: Return true if the version is outside
the bounds of the range in either the high or low direction. The
`hilo` argument must be either the string `'>'` or `'<'`. (This is
the function called by `gtr` and `ltr`.)
* `intersects(range)`: Return true if any of the range comparators intersect.
* `simplifyRange(versions, range)`: Return a "simplified" range that
matches the same items in the `versions` list as the range specified. Note
that it does *not* guarantee that it would match the same versions in all
cases, only for the set of versions provided. This is useful when
generating ranges by joining together multiple versions with `||`
programmatically, to provide the user with something a bit more
ergonomic. If the provided range is shorter in string-length than the
generated range, then that is returned.
* `subset(subRange, superRange)`: Return `true` if the `subRange` range is
entirely contained by the `superRange` range.
Note that, since ranges may be non-contiguous, a version might not be
greater than a range, less than a range, *or* satisfy a range! For
example, the range `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9`
until `2.0.0`, so version `1.2.10` would not be greater than the
range (because `2.0.1` satisfies, which is higher), nor less than the
range (since `1.2.8` satisfies, which is lower), and it also does not
satisfy the range.
If you want to know if a version satisfies or does not satisfy a
range, use the `satisfies(version, range)` function.
### Coercion
* `coerce(version, options)`: Coerces a string to semver if possible
This aims to provide a very forgiving translation of a non-semver string to
semver. It looks for the first digit in a string and consumes all
remaining characters which satisfy at least a partial semver (e.g., `1`,
`1.2`, `1.2.3`) up to the max permitted length (256 characters). Longer
versions are simply truncated (`4.6.3.9.2-alpha2` becomes `4.6.3`). All
surrounding text is simply ignored (`v3.4 replaces v3.3.1` becomes
`3.4.0`). Only text which lacks digits will fail coercion (`version one`
is not valid). The maximum length for any semver component considered for
coercion is 16 characters; longer components will be ignored
(`10000000000000000.4.7.4` becomes `4.7.4`). The maximum value for any
semver component is `Number.MAX_SAFE_INTEGER || (2**53 - 1)`; higher value
components are invalid (`9999999999999999.4.7.4` is likely invalid).
If the `options.rtl` flag is set, then `coerce` will return the right-most
coercible tuple that does not share an ending index with a longer coercible
tuple. For example, `1.2.3.4` will return `2.3.4` in rtl mode, not
`4.0.0`. `1.2.3/4` will return `4.0.0`, because the `4` is not a part of
any other overlapping SemVer tuple.
If the `options.includePrerelease` flag is set, then the `coerce` result will contain
prerelease and build parts of a version. For example, `1.2.3.4-rc.1+rev.2`
will preserve prerelease `rc.1` and build `rev.2` in the result.
### Clean
* `clean(version)`: Clean a string to be a valid semver if possible
This will return a cleaned and trimmed semver version. If the provided
version is not valid a null will be returned. This does not work for
ranges.
ex.
* `s.clean(' = v 2.1.5foo')`: `null`
* `s.clean(' = v 2.1.5foo', { loose: true })`: `'2.1.5-foo'`
* `s.clean(' = v 2.1.5-foo')`: `null`
* `s.clean(' = v 2.1.5-foo', { loose: true })`: `'2.1.5-foo'`
* `s.clean('=v2.1.5')`: `'2.1.5'`
* `s.clean(' =v2.1.5')`: `'2.1.5'`
* `s.clean(' 2.1.5 ')`: `'2.1.5'`
* `s.clean('~1.0.0')`: `null`
## Constants
As a convenience, helper constants are exported to provide information about what `node-semver` supports:
### `RELEASE_TYPES`
- major
- premajor
- minor
- preminor
- patch
- prepatch
- prerelease
```
const semver = require('semver');
if (semver.RELEASE_TYPES.includes(arbitraryUserInput)) {
console.log('This is a valid release type!');
} else {
console.warn('This is NOT a valid release type!');
}
```
### `SEMVER_SPEC_VERSION`
2.0.0
```
const semver = require('semver');
console.log('We are currently using the semver specification version:', semver.SEMVER_SPEC_VERSION);
```
## Exported Modules
<!--
TODO: Make sure that all of these items are documented (classes aren't,
eg), and then pull the module name into the documentation for that specific
thing.
-->
You may pull in just the part of this semver utility that you need if you
are sensitive to packing and tree-shaking concerns. The main
`require('semver')` export uses getter functions to lazily load the parts
of the API that are used.
The following modules are available:
* `require('semver')`
* `require('semver/classes')`
* `require('semver/classes/comparator')`
* `require('semver/classes/range')`
* `require('semver/classes/semver')`
* `require('semver/functions/clean')`
* `require('semver/functions/cmp')`
* `require('semver/functions/coerce')`
* `require('semver/functions/compare')`
* `require('semver/functions/compare-build')`
* `require('semver/functions/compare-loose')`
* `require('semver/functions/diff')`
* `require('semver/functions/eq')`
* `require('semver/functions/gt')`
* `require('semver/functions/gte')`
* `require('semver/functions/inc')`
* `require('semver/functions/lt')`
* `require('semver/functions/lte')`
* `require('semver/functions/major')`
* `require('semver/functions/minor')`
* `require('semver/functions/neq')`
* `require('semver/functions/parse')`
* `require('semver/functions/patch')`
* `require('semver/functions/prerelease')`
* `require('semver/functions/rcompare')`
* `require('semver/functions/rsort')`
* `require('semver/functions/satisfies')`
* `require('semver/functions/sort')`
* `require('semver/functions/valid')`
* `require('semver/ranges/gtr')`
* `require('semver/ranges/intersects')`
* `require('semver/ranges/ltr')`
* `require('semver/ranges/max-satisfying')`
* `require('semver/ranges/min-satisfying')`
* `require('semver/ranges/min-version')`
* `require('semver/ranges/outside')`
* `require('semver/ranges/simplify')`
* `require('semver/ranges/subset')`
* `require('semver/ranges/to-comparators')`
* `require('semver/ranges/valid')`

View File

@@ -0,0 +1,38 @@
import { AnyRouter } from './router.js';
import { ParsedLocation } from './location.js';
import { NonNullableUpdater } from './utils.js';
export type ScrollRestorationEntry = {
scrollX: number;
scrollY: number;
};
export type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>;
export type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>;
export type ScrollRestorationCache = {
state: ScrollRestorationByKey;
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void;
};
export type ScrollRestorationOptions = {
getKey?: (location: ParsedLocation) => string;
scrollBehavior?: ScrollToOptions['behavior'];
};
export declare const storageKey = "tsr-scroll-restoration-v1_3";
export declare const scrollRestorationCache: ScrollRestorationCache;
/**
* The default `getKey` function for `useScrollRestoration`.
* It returns the `key` from the location state or the `href` of the location.
*
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
*/
export declare const defaultGetScrollRestorationKey: (location: ParsedLocation) => string;
export declare function getCssSelector(el: any): string;
export declare function restoreScroll(storageKey: string, key: string | undefined, behavior: ScrollToOptions['behavior'] | undefined, shouldScrollRestoration: boolean | undefined, scrollToTopSelectors: Array<string> | undefined): void;
export declare function setupScrollRestoration(router: AnyRouter, force?: boolean): void;
/**
* @internal
* Handles hash-based scrolling after navigation completes.
* To be used in framework-specific <Transitioner> components during the onResolved event.
*
* Provides hash scrolling for programmatic navigation when default browser handling is prevented.
* @param router The router instance containing current location and state
*/
export declare function handleHashScroll(router: AnyRouter): void;

View File

@@ -0,0 +1,44 @@
{
"name": "esutils",
"description": "utility box for ECMAScript language tools",
"homepage": "https://github.com/estools/esutils",
"main": "lib/utils.js",
"version": "2.0.3",
"engines": {
"node": ">=0.10.0"
},
"directories": {
"lib": "./lib"
},
"files": [
"LICENSE.BSD",
"README.md",
"lib"
],
"maintainers": [
{
"name": "Yusuke Suzuki",
"email": "utatane.tea@gmail.com",
"web": "http://github.com/Constellation"
}
],
"repository": {
"type": "git",
"url": "http://github.com/estools/esutils.git"
},
"devDependencies": {
"chai": "~1.7.2",
"coffee-script": "~1.6.3",
"jshint": "2.6.3",
"mocha": "~2.2.1",
"regenerate": "~1.3.1",
"unicode-9.0.0": "~0.7.0"
},
"license": "BSD-2-Clause",
"scripts": {
"test": "npm run-script lint && npm run-script unit-test",
"lint": "jshint lib/*.js",
"unit-test": "mocha --compilers coffee:coffee-script -R spec",
"generate-regex": "node tools/generate-identifier-regex.js"
}
}

View File

@@ -0,0 +1 @@
module.exports={A:{A:{"2":"K D E F A B mC"},B:{"2":"C L","132":"M G N O P","322":"0 9 Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I"},C:{"2":"1 2 nC LC J PB K D E F A B C L M G N O P QB qC rC","132":"3 4 5 6 7 8 RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC","194":"0 9 wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC oC pC"},D:{"2":"1 2 3 4 5 6 7 8 J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB","322":"0 9 vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC"},E:{"2":"J PB K D E F A B C L M G sC SC tC uC vC wC TC FC GC xC yC zC UC VC HC 0C IC WC XC YC ZC aC 1C JC bC cC dC eC fC 2C KC gC hC iC jC 3C"},F:{"2":"1 2 3 4 5 6 7 8 F B C G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB wB xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 4C 5C 6C 7C FC kC 8C GC","322":"0 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z"},G:{"2":"E SC 9C lC AD BD CD DD ED FD GD HD ID JD KD LD MD ND OD PD QD RD SD UC VC HC TD IC WC XC YC ZC aC UD JC bC cC dC eC fC VD KC gC hC iC jC"},H:{"2":"WD"},I:{"2":"LC J I XD YD ZD aD lC bD cD"},J:{"2":"D A"},K:{"2":"A B C H FC kC GC"},L:{"2":"I"},M:{"1":"EC"},N:{"2":"A B"},O:{"2":"HC"},P:{"2":"1 2 3 4 5 6 7 8 J dD eD fD gD hD TC iD jD kD lD mD IC JC KC nD"},Q:{"2":"oD"},R:{"2":"pD"},S:{"132":"qD rD"}},B:4,C:"Ambient Light Sensor",D:true};

View File

@@ -0,0 +1,16 @@
/**
* @license React
* react-compiler-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
"use strict";
var ReactSharedInternals =
require("react").__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
exports.c = function (size) {
return ReactSharedInternals.H.useMemoCache(size);
};

View File

@@ -0,0 +1,442 @@
// @ts-check
/** @typedef {import('./index').Visitor} Visitor */
/**
* Composes multiple visitor objects into a single one.
* @param {Visitor[]} visitors
* @return {Visitor}
*/
function composeVisitors(visitors) {
if (visitors.length === 1) {
return visitors[0];
}
/** @type Visitor */
let res = {};
composeSimpleVisitors(res, visitors, 'StyleSheet');
composeSimpleVisitors(res, visitors, 'StyleSheetExit');
composeObjectVisitors(res, visitors, 'Rule', ruleVisitor, wrapCustomAndUnknownAtRule);
composeObjectVisitors(res, visitors, 'RuleExit', ruleVisitor, wrapCustomAndUnknownAtRule);
composeObjectVisitors(res, visitors, 'Declaration', declarationVisitor, wrapCustomProperty);
composeObjectVisitors(res, visitors, 'DeclarationExit', declarationVisitor, wrapCustomProperty);
composeSimpleVisitors(res, visitors, 'Url');
composeSimpleVisitors(res, visitors, 'Color');
composeSimpleVisitors(res, visitors, 'Image');
composeSimpleVisitors(res, visitors, 'ImageExit');
composeSimpleVisitors(res, visitors, 'Length');
composeSimpleVisitors(res, visitors, 'Angle');
composeSimpleVisitors(res, visitors, 'Ratio');
composeSimpleVisitors(res, visitors, 'Resolution');
composeSimpleVisitors(res, visitors, 'Time');
composeSimpleVisitors(res, visitors, 'CustomIdent');
composeSimpleVisitors(res, visitors, 'DashedIdent');
composeArrayFunctions(res, visitors, 'MediaQuery');
composeArrayFunctions(res, visitors, 'MediaQueryExit');
composeSimpleVisitors(res, visitors, 'SupportsCondition');
composeSimpleVisitors(res, visitors, 'SupportsConditionExit');
composeArrayFunctions(res, visitors, 'Selector');
composeTokenVisitors(res, visitors, 'Token', 'token', false);
composeTokenVisitors(res, visitors, 'Function', 'function', false);
composeTokenVisitors(res, visitors, 'FunctionExit', 'function', true);
composeTokenVisitors(res, visitors, 'Variable', 'var', false);
composeTokenVisitors(res, visitors, 'VariableExit', 'var', true);
composeTokenVisitors(res, visitors, 'EnvironmentVariable', 'env', false);
composeTokenVisitors(res, visitors, 'EnvironmentVariableExit', 'env', true);
return res;
}
module.exports = composeVisitors;
function wrapCustomAndUnknownAtRule(k, f) {
if (k === 'unknown') {
return (value => f({ type: 'unknown', value }));
}
if (k === 'custom') {
return (value => f({ type: 'custom', value }));
}
return f;
}
function wrapCustomProperty(k, f) {
return k === 'custom' ? (value => f({ property: 'custom', value })) : f;
}
/**
* @param {import('./index').Visitor['Rule']} f
* @param {import('./ast').Rule} item
*/
function ruleVisitor(f, item) {
if (typeof f === 'object') {
if (item.type === 'unknown') {
let v = f.unknown;
if (typeof v === 'object') {
v = v[item.value.name];
}
return v?.(item.value);
}
if (item.type === 'custom') {
let v = f.custom;
if (typeof v === 'object') {
v = v[item.value.name];
}
return v?.(item.value);
}
return f[item.type]?.(item);
}
return f?.(item);
}
/**
* @param {import('./index').Visitor['Declaration']} f
* @param {import('./ast').Declaration} item
*/
function declarationVisitor(f, item) {
if (typeof f === 'object') {
/** @type {string} */
let name = item.property;
if (item.property === 'unparsed') {
name = item.value.propertyId.property;
} else if (item.property === 'custom') {
let v = f.custom;
if (typeof v === 'object') {
v = v[item.value.name];
}
return v?.(item.value);
}
return f[name]?.(item);
}
return f?.(item);
}
/**
*
* @param {Visitor[]} visitors
* @param {string} key
* @returns {[any[], boolean, Set<string>]}
*/
function extractObjectsOrFunctions(visitors, key) {
let values = [];
let hasFunction = false;
let allKeys = new Set();
for (let visitor of visitors) {
let v = visitor[key];
if (v) {
if (typeof v === 'function') {
hasFunction = true;
} else {
for (let key in v) {
allKeys.add(key);
}
}
values.push(v);
}
}
return [values, hasFunction, allKeys];
}
/**
* @template {keyof Visitor} K
* @param {Visitor} res
* @param {Visitor[]} visitors
* @param {K} key
* @param {(visitor: Visitor[K], item: any) => any | any[] | void} apply
* @param {(k: string, f: any) => any} wrapKey
*/
function composeObjectVisitors(res, visitors, key, apply, wrapKey) {
let [values, hasFunction, allKeys] = extractObjectsOrFunctions(visitors, key);
if (values.length === 0) {
return;
}
if (values.length === 1) {
res[key] = values[0];
return;
}
let f = createArrayVisitor(visitors, (visitor, item) => apply(visitor[key], item));
if (hasFunction) {
res[key] = f;
} else {
/** @type {any} */
let v = {};
for (let k of allKeys) {
v[k] = wrapKey(k, f);
}
res[key] = v;
}
}
/**
* @param {Visitor} res
* @param {Visitor[]} visitors
* @param {string} key
* @param {import('./ast').TokenOrValue['type']} type
* @param {boolean} isExit
*/
function composeTokenVisitors(res, visitors, key, type, isExit) {
let [values, hasFunction, allKeys] = extractObjectsOrFunctions(visitors, key);
if (values.length === 0) {
return;
}
if (values.length === 1) {
res[key] = values[0];
return;
}
let f = createTokenVisitor(visitors, type, isExit);
if (hasFunction) {
res[key] = f;
} else {
let v = {};
for (let key of allKeys) {
v[key] = f;
}
res[key] = v;
}
}
/**
* @param {Visitor[]} visitors
* @param {import('./ast').TokenOrValue['type']} type
*/
function createTokenVisitor(visitors, type, isExit) {
let v = createArrayVisitor(visitors, (visitor, /** @type {import('./ast').TokenOrValue} */ item) => {
let f;
switch (item.type) {
case 'token':
f = visitor.Token;
if (typeof f === 'object') {
f = f[item.value.type];
}
break;
case 'function':
f = isExit ? visitor.FunctionExit : visitor.Function;
if (typeof f === 'object') {
f = f[item.value.name];
}
break;
case 'var':
f = isExit ? visitor.VariableExit : visitor.Variable;
break;
case 'env':
f = isExit ? visitor.EnvironmentVariableExit : visitor.EnvironmentVariable;
if (typeof f === 'object') {
let name;
switch (item.value.name.type) {
case 'ua':
case 'unknown':
name = item.value.name.value;
break;
case 'custom':
name = item.value.name.ident;
break;
}
f = f[name];
}
break;
case 'color':
f = visitor.Color;
break;
case 'url':
f = visitor.Url;
break;
case 'length':
f = visitor.Length;
break;
case 'angle':
f = visitor.Angle;
break;
case 'time':
f = visitor.Time;
break;
case 'resolution':
f = visitor.Resolution;
break;
case 'dashed-ident':
f = visitor.DashedIdent;
break;
}
if (!f) {
return;
}
let res = f(item.value);
switch (item.type) {
case 'color':
case 'url':
case 'length':
case 'angle':
case 'time':
case 'resolution':
case 'dashed-ident':
if (Array.isArray(res)) {
res = res.map(value => ({ type: item.type, value }))
} else if (res) {
res = { type: item.type, value: res };
}
break;
}
return res;
});
return value => v({ type, value });
}
/**
* @param {Visitor[]} visitors
* @param {string} key
*/
function extractFunctions(visitors, key) {
let functions = [];
for (let visitor of visitors) {
let f = visitor[key];
if (f) {
functions.push(f);
}
}
return functions;
}
/**
* @param {Visitor} res
* @param {Visitor[]} visitors
* @param {string} key
*/
function composeSimpleVisitors(res, visitors, key) {
let functions = extractFunctions(visitors, key);
if (functions.length === 0) {
return;
}
if (functions.length === 1) {
res[key] = functions[0];
return;
}
res[key] = arg => {
let mutated = false;
for (let f of functions) {
let res = f(arg);
if (res) {
arg = res;
mutated = true;
}
}
return mutated ? arg : undefined;
};
}
/**
* @param {Visitor} res
* @param {Visitor[]} visitors
* @param {string} key
*/
function composeArrayFunctions(res, visitors, key) {
let functions = extractFunctions(visitors, key);
if (functions.length === 0) {
return;
}
if (functions.length === 1) {
res[key] = functions[0];
return;
}
res[key] = createArrayVisitor(functions, (f, item) => f(item));
}
/**
* @template T
* @template V
* @param {T[]} visitors
* @param {(visitor: T, item: V) => V | V[] | void} apply
* @returns {(item: V) => V | V[] | void}
*/
function createArrayVisitor(visitors, apply) {
let seen = new Bitset(visitors.length);
return arg => {
let arr = [arg];
let mutated = false;
seen.clear();
for (let i = 0; i < arr.length; i++) {
// For each value, call all visitors. If a visitor returns a new value,
// we start over, but skip the visitor that generated the value or saw
// it before (to avoid cycles). This way, visitors can be composed in any order.
for (let v = 0; v < visitors.length;) {
if (seen.get(v)) {
v++;
continue;
}
let item = arr[i];
let visitor = visitors[v];
let res = apply(visitor, item);
if (Array.isArray(res)) {
if (res.length === 0) {
arr.splice(i, 1);
} else if (res.length === 1) {
arr[i] = res[0];
} else {
arr.splice(i, 1, ...res);
}
mutated = true;
seen.set(v);
v = 0;
} else if (res) {
arr[i] = res;
mutated = true;
seen.set(v);
v = 0;
} else {
v++;
}
}
}
if (!mutated) {
return;
}
return arr.length === 1 ? arr[0] : arr;
};
}
class Bitset {
constructor(maxBits = 32) {
this.bits = 0;
this.more = maxBits > 32 ? new Uint32Array(Math.ceil((maxBits - 32) / 32)) : null;
}
/** @param {number} bit */
get(bit) {
if (bit >= 32 && this.more) {
let i = Math.floor((bit - 32) / 32);
let b = bit % 32;
return Boolean(this.more[i] & (1 << b));
} else {
return Boolean(this.bits & (1 << bit));
}
}
/** @param {number} bit */
set(bit) {
if (bit >= 32 && this.more) {
let i = Math.floor((bit - 32) / 32);
let b = bit % 32;
this.more[i] |= 1 << b;
} else {
this.bits |= 1 << bit;
}
}
clear() {
this.bits = 0;
if (this.more) {
this.more.fill(0);
}
}
}

View File

@@ -0,0 +1,221 @@
/**
* @fileoverview Rule to disallow unsafe optional chaining
* @author Yeon JuAn
*/
"use strict";
const UNSAFE_ARITHMETIC_OPERATORS = new Set(["+", "-", "/", "*", "%", "**"]);
const UNSAFE_ASSIGNMENT_OPERATORS = new Set([
"+=",
"-=",
"/=",
"*=",
"%=",
"**=",
]);
const UNSAFE_RELATIONAL_OPERATORS = new Set(["in", "instanceof"]);
/**
* Checks whether a node is a destructuring pattern or not
* @param {ASTNode} node node to check
* @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
*/
function isDestructuringPattern(node) {
return node.type === "ObjectPattern" || node.type === "ArrayPattern";
}
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: "problem",
defaultOptions: [
{
disallowArithmeticOperators: false,
},
],
docs: {
description:
"Disallow use of optional chaining in contexts where the `undefined` value is not allowed",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining",
},
schema: [
{
type: "object",
properties: {
disallowArithmeticOperators: {
type: "boolean",
},
},
additionalProperties: false,
},
],
fixable: null,
messages: {
unsafeOptionalChain:
"Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
unsafeArithmetic:
"Unsafe arithmetic operation on optional chaining. It can result in NaN.",
},
},
create(context) {
const [{ disallowArithmeticOperators }] = context.options;
/**
* Reports unsafe usage of optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeUsage(node) {
context.report({
messageId: "unsafeOptionalChain",
node,
});
}
/**
* Reports unsafe arithmetic operation on optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeArithmetic(node) {
context.report({
messageId: "unsafeArithmetic",
node,
});
}
/**
* Checks and reports if a node can short-circuit with `undefined` by optional chaining.
* @param {ASTNode} [node] node to check
* @param {Function} reportFunc report function
* @returns {void}
*/
function checkUndefinedShortCircuit(node, reportFunc) {
if (!node) {
return;
}
switch (node.type) {
case "LogicalExpression":
if (node.operator === "||" || node.operator === "??") {
checkUndefinedShortCircuit(node.right, reportFunc);
} else if (node.operator === "&&") {
checkUndefinedShortCircuit(node.left, reportFunc);
checkUndefinedShortCircuit(node.right, reportFunc);
}
break;
case "SequenceExpression":
checkUndefinedShortCircuit(
node.expressions.at(-1),
reportFunc,
);
break;
case "ConditionalExpression":
checkUndefinedShortCircuit(node.consequent, reportFunc);
checkUndefinedShortCircuit(node.alternate, reportFunc);
break;
case "AwaitExpression":
checkUndefinedShortCircuit(node.argument, reportFunc);
break;
case "ChainExpression":
reportFunc(node);
break;
default:
break;
}
}
/**
* Checks unsafe usage of optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeUsage(node) {
checkUndefinedShortCircuit(node, reportUnsafeUsage);
}
/**
* Checks unsafe arithmetic operations on optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeArithmetic(node) {
checkUndefinedShortCircuit(node, reportUnsafeArithmetic);
}
return {
"AssignmentExpression, AssignmentPattern"(node) {
if (isDestructuringPattern(node.left)) {
checkUnsafeUsage(node.right);
}
},
"ClassDeclaration, ClassExpression"(node) {
checkUnsafeUsage(node.superClass);
},
CallExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.callee);
}
},
NewExpression(node) {
checkUnsafeUsage(node.callee);
},
VariableDeclarator(node) {
if (isDestructuringPattern(node.id)) {
checkUnsafeUsage(node.init);
}
},
MemberExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.object);
}
},
TaggedTemplateExpression(node) {
checkUnsafeUsage(node.tag);
},
ForOfStatement(node) {
checkUnsafeUsage(node.right);
},
SpreadElement(node) {
if (node.parent && node.parent.type !== "ObjectExpression") {
checkUnsafeUsage(node.argument);
}
},
BinaryExpression(node) {
if (UNSAFE_RELATIONAL_OPERATORS.has(node.operator)) {
checkUnsafeUsage(node.right);
}
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
checkUnsafeArithmetic(node.left);
}
},
WithStatement(node) {
checkUnsafeUsage(node.object);
},
UnaryExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.argument);
}
},
AssignmentExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ASSIGNMENT_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
}
},
};
},
};

View File

@@ -0,0 +1,598 @@
/**
* @fileoverview Flat config schema
* @author Nicholas C. Zakas
*/
"use strict";
//-----------------------------------------------------------------------------
// Requirements
//-----------------------------------------------------------------------------
const { normalizeSeverityToNumber } = require("../shared/severity");
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
* @typedef ObjectPropertySchema
* @property {Function|string} merge The function or name of the function to call
* to merge multiple objects with this property.
* @property {Function|string} validate The function or name of the function to call
* to validate the value of this property.
*/
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
const ruleSeverities = new Map([
[0, 0],
["off", 0],
[1, 1],
["warn", 1],
[2, 2],
["error", 2],
]);
/**
* Check if a value is a non-null object.
* @param {any} value The value to check.
* @returns {boolean} `true` if the value is a non-null object.
*/
function isNonNullObject(value) {
return typeof value === "object" && value !== null;
}
/**
* Check if a value is a non-null non-array object.
* @param {any} value The value to check.
* @returns {boolean} `true` if the value is a non-null non-array object.
*/
function isNonArrayObject(value) {
return isNonNullObject(value) && !Array.isArray(value);
}
/**
* Check if a value is undefined.
* @param {any} value The value to check.
* @returns {boolean} `true` if the value is undefined.
*/
function isUndefined(value) {
return typeof value === "undefined";
}
/**
* Deeply merges two non-array objects.
* @param {Object} first The base object.
* @param {Object} second The overrides object.
* @param {Map<string, Map<string, Object>>} [mergeMap] Maps the combination of first and second arguments to a merged result.
* @returns {Object} An object with properties from both first and second.
*/
function deepMerge(first, second, mergeMap = new Map()) {
let secondMergeMap = mergeMap.get(first);
if (secondMergeMap) {
const result = secondMergeMap.get(second);
if (result) {
// If this combination of first and second arguments has been already visited, return the previously created result.
return result;
}
} else {
secondMergeMap = new Map();
mergeMap.set(first, secondMergeMap);
}
/*
* First create a result object where properties from the second object
* overwrite properties from the first. This sets up a baseline to use
* later rather than needing to inspect and change every property
* individually.
*/
const result = {
...first,
...second,
};
delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
// Store the pending result for this combination of first and second arguments.
secondMergeMap.set(second, result);
for (const key of Object.keys(second)) {
// avoid hairy edge case
if (
key === "__proto__" ||
!Object.prototype.propertyIsEnumerable.call(first, key)
) {
continue;
}
const firstValue = first[key];
const secondValue = second[key];
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
result[key] = deepMerge(firstValue, secondValue, mergeMap);
} else if (isUndefined(secondValue)) {
result[key] = firstValue;
}
}
return result;
}
/**
* Normalizes the rule options config for a given rule by ensuring that
* it is an array and that the first item is 0, 1, or 2.
* @param {Array|string|number} ruleOptions The rule options config.
* @returns {Array} An array of rule options.
*/
function normalizeRuleOptions(ruleOptions) {
const finalOptions = Array.isArray(ruleOptions)
? ruleOptions.slice(0)
: [ruleOptions];
finalOptions[0] = ruleSeverities.get(finalOptions[0]);
return structuredClone(finalOptions);
}
/**
* Determines if an object has any methods.
* @param {Object} object The object to check.
* @returns {boolean} `true` if the object has any methods.
*/
function hasMethod(object) {
for (const key of Object.keys(object)) {
if (typeof object[key] === "function") {
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Assertions
//-----------------------------------------------------------------------------
/**
* The error type when a rule's options are configured with an invalid type.
*/
class InvalidRuleOptionsError extends Error {
/**
* @param {string} ruleId Rule name being configured.
* @param {any} value The invalid value.
*/
constructor(ruleId, value) {
super(
`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`,
);
this.messageTemplate = "invalid-rule-options";
this.messageData = { ruleId, value };
}
}
/**
* Validates that a value is a valid rule options entry.
* @param {string} ruleId Rule name being configured.
* @param {any} value The value to check.
* @returns {void}
* @throws {InvalidRuleOptionsError} If the value isn't a valid rule options.
*/
function assertIsRuleOptions(ruleId, value) {
if (
typeof value !== "string" &&
typeof value !== "number" &&
!Array.isArray(value)
) {
throw new InvalidRuleOptionsError(ruleId, value);
}
}
/**
* The error type when a rule's severity is invalid.
*/
class InvalidRuleSeverityError extends Error {
/**
* @param {string} ruleId Rule name being configured.
* @param {any} value The invalid value.
*/
constructor(ruleId, value) {
super(
`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`,
);
this.messageTemplate = "invalid-rule-severity";
this.messageData = { ruleId, value };
}
}
/**
* Validates that a value is valid rule severity.
* @param {string} ruleId Rule name being configured.
* @param {any} value The value to check.
* @returns {void}
* @throws {InvalidRuleSeverityError} If the value isn't a valid rule severity.
*/
function assertIsRuleSeverity(ruleId, value) {
const severity = ruleSeverities.get(value);
if (typeof severity === "undefined") {
throw new InvalidRuleSeverityError(ruleId, value);
}
}
/**
* Validates that a given string is the form pluginName/objectName.
* @param {string} value The string to check.
* @returns {void}
* @throws {TypeError} If the string isn't in the correct format.
*/
function assertIsPluginMemberName(value) {
if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
throw new TypeError(
`Expected string in the form "pluginName/objectName" but found "${value}".`,
);
}
}
/**
* Validates that a value is an object.
* @param {any} value The value to check.
* @returns {void}
* @throws {TypeError} If the value isn't an object.
*/
function assertIsObject(value) {
if (!isNonNullObject(value)) {
throw new TypeError("Expected an object.");
}
}
/**
* The error type when there's an eslintrc-style options in a flat config.
*/
class IncompatibleKeyError extends Error {
/**
* @param {string} key The invalid key.
*/
constructor(key) {
super(
"This appears to be in eslintrc format rather than flat config format.",
);
this.messageTemplate = "eslintrc-incompat";
this.messageData = { key };
}
}
/**
* The error type when there's an eslintrc-style plugins array found.
*/
class IncompatiblePluginsError extends Error {
/**
* Creates a new instance.
* @param {Array<string>} plugins The plugins array.
*/
constructor(plugins) {
super(
"This appears to be in eslintrc format (array of strings) rather than flat config format (object).",
);
this.messageTemplate = "eslintrc-plugins";
this.messageData = { plugins };
}
}
//-----------------------------------------------------------------------------
// Low-Level Schemas
//-----------------------------------------------------------------------------
/** @type {ObjectPropertySchema} */
const booleanSchema = {
merge: "replace",
validate: "boolean",
};
const ALLOWED_SEVERITIES = new Set(["error", "warn", "off", 2, 1, 0]);
/** @type {ObjectPropertySchema} */
const disableDirectiveSeveritySchema = {
merge(first, second) {
const value = second === void 0 ? first : second;
if (typeof value === "boolean") {
return value ? "warn" : "off";
}
return normalizeSeverityToNumber(value);
},
validate(value) {
if (!(ALLOWED_SEVERITIES.has(value) || typeof value === "boolean")) {
throw new TypeError(
'Expected one of: "error", "warn", "off", 0, 1, 2, or a boolean.',
);
}
},
};
/** @type {ObjectPropertySchema} */
const unusedInlineConfigsSeveritySchema = {
merge(first, second) {
const value = second === void 0 ? first : second;
return normalizeSeverityToNumber(value);
},
validate(value) {
if (!ALLOWED_SEVERITIES.has(value)) {
throw new TypeError(
'Expected one of: "error", "warn", "off", 0, 1, or 2.',
);
}
},
};
/** @type {ObjectPropertySchema} */
const deepObjectAssignSchema = {
merge(first = {}, second = {}) {
return deepMerge(first, second);
},
validate: "object",
};
//-----------------------------------------------------------------------------
// High-Level Schemas
//-----------------------------------------------------------------------------
/** @type {ObjectPropertySchema} */
const languageOptionsSchema = {
merge(first = {}, second = {}) {
const result = deepMerge(first, second);
for (const [key, value] of Object.entries(result)) {
/*
* Special case: Because the `parser` property is an object, it should
* not be deep merged. Instead, it should be replaced if it exists in
* the second object. To make this more generic, we just check for
* objects with methods and replace them if they exist in the second
* object.
*/
if (isNonArrayObject(value)) {
if (hasMethod(value)) {
result[key] = second[key] ?? first[key];
continue;
}
// for other objects, make sure we aren't reusing the same object
result[key] = { ...result[key] };
continue;
}
}
return result;
},
validate: "object",
};
/** @type {ObjectPropertySchema} */
const languageSchema = {
merge: "replace",
validate: assertIsPluginMemberName,
};
/** @type {ObjectPropertySchema} */
const pluginsSchema = {
merge(first = {}, second = {}) {
const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
const result = {};
// manually validate that plugins are not redefined
for (const key of keys) {
// avoid hairy edge case
if (key === "__proto__") {
continue;
}
if (key in first && key in second && first[key] !== second[key]) {
throw new TypeError(`Cannot redefine plugin "${key}".`);
}
result[key] = second[key] || first[key];
}
return result;
},
validate(value) {
// first check the value to be sure it's an object
if (value === null || typeof value !== "object") {
throw new TypeError("Expected an object.");
}
// make sure it's not an array, which would mean eslintrc-style is used
if (Array.isArray(value)) {
throw new IncompatiblePluginsError(value);
}
// second check the keys to make sure they are objects
for (const key of Object.keys(value)) {
// avoid hairy edge case
if (key === "__proto__") {
continue;
}
if (value[key] === null || typeof value[key] !== "object") {
throw new TypeError(`Key "${key}": Expected an object.`);
}
}
},
};
/** @type {ObjectPropertySchema} */
const processorSchema = {
merge: "replace",
validate(value) {
if (typeof value === "string") {
assertIsPluginMemberName(value);
} else if (value && typeof value === "object") {
if (
typeof value.preprocess !== "function" ||
typeof value.postprocess !== "function"
) {
throw new TypeError(
"Object must have a preprocess() and a postprocess() method.",
);
}
} else {
throw new TypeError("Expected an object or a string.");
}
},
};
/** @type {ObjectPropertySchema} */
const rulesSchema = {
merge(first = {}, second = {}) {
const result = {
...first,
...second,
};
for (const ruleId of Object.keys(result)) {
try {
// avoid hairy edge case
if (ruleId === "__proto__") {
/* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
delete result.__proto__;
continue;
}
result[ruleId] = normalizeRuleOptions(result[ruleId]);
/*
* If either rule config is missing, then the correct
* config is already present and we just need to normalize
* the severity.
*/
if (!(ruleId in first) || !(ruleId in second)) {
continue;
}
const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
/*
* If the second rule config only has a severity (length of 1),
* then use that severity and keep the rest of the options from
* the first rule config.
*/
if (secondRuleOptions.length === 1) {
result[ruleId] = [
secondRuleOptions[0],
...firstRuleOptions.slice(1),
];
continue;
}
/*
* In any other situation, then the second rule config takes
* precedence. That means the value at `result[ruleId]` is
* already correct and no further work is necessary.
*/
} catch (ex) {
throw new Error(`Key "${ruleId}": ${ex.message}`, {
cause: ex,
});
}
}
return result;
},
validate(value) {
assertIsObject(value);
/*
* We are not checking the rule schema here because there is no
* guarantee that the rule definition is present at this point. Instead
* we wait and check the rule schema during the finalization step
* of calculating a config.
*/
for (const ruleId of Object.keys(value)) {
// avoid hairy edge case
if (ruleId === "__proto__") {
continue;
}
const ruleOptions = value[ruleId];
assertIsRuleOptions(ruleId, ruleOptions);
if (Array.isArray(ruleOptions)) {
assertIsRuleSeverity(ruleId, ruleOptions[0]);
} else {
assertIsRuleSeverity(ruleId, ruleOptions);
}
}
},
};
/**
* Creates a schema that always throws an error. Useful for warning
* about eslintrc-style keys.
* @param {string} key The eslintrc key to create a schema for.
* @returns {ObjectPropertySchema} The schema.
*/
function createEslintrcErrorSchema(key) {
return {
merge: "replace",
validate() {
throw new IncompatibleKeyError(key);
},
};
}
const eslintrcKeys = [
"env",
"extends",
"globals",
"ignorePatterns",
"noInlineConfig",
"overrides",
"parser",
"parserOptions",
"reportUnusedDisableDirectives",
"root",
];
//-----------------------------------------------------------------------------
// Full schema
//-----------------------------------------------------------------------------
const flatConfigSchema = {
// eslintrc-style keys that should always error
...Object.fromEntries(
eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)]),
),
// flat config keys
settings: deepObjectAssignSchema,
linterOptions: {
schema: {
noInlineConfig: booleanSchema,
reportUnusedDisableDirectives: disableDirectiveSeveritySchema,
reportUnusedInlineConfigs: unusedInlineConfigsSeveritySchema,
},
},
language: languageSchema,
languageOptions: languageOptionsSchema,
processor: processorSchema,
plugins: pluginsSchema,
rules: rulesSchema,
};
//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
module.exports = {
flatConfigSchema,
hasMethod,
assertIsRuleSeverity,
};

View File

@@ -0,0 +1,21 @@
Copyright (c) 2013 Kael Zhang <i@kael.me>, contributors
http://kael.me/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,21 @@
/**
* @fileoverview Assertion utilities equivalent to the Node.js node:asserts module.
* @author Josh Goldberg
*/
"use strict";
/**
* Throws an error if the input is not truthy.
* @param {unknown} value The input that is checked for being truthy.
* @param {string} message Message to throw if the input is not truthy.
* @returns {void}
* @throws {Error} When the condition is not truthy.
*/
function ok(value, message = "Assertion failed.") {
if (!value) {
throw new Error(message);
}
}
module.exports = ok;

View File

@@ -0,0 +1,18 @@
# mkdirp-classic
Just a non-deprecated mirror of [mkdirp 0.5.2](https://github.com/substack/node-mkdirp/tree/0.5.1)
for use in modules where we depend on the non promise interface.
```
npm install mkdirp-classic
```
## Usage
``` js
// See the above link
```
## License
MIT