routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Yosuke Ota
|
||||
|
||||
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.
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
# Introduction
|
||||
|
||||
[eslint-plugin-jsonc](https://www.npmjs.com/package/eslint-plugin-jsonc) is ESLint plugin for [JSON], [JSONC] and [JSON5] files.
|
||||
|
||||
[](https://www.npmjs.com/package/eslint-plugin-jsonc)
|
||||
[](https://www.npmjs.com/package/eslint-plugin-jsonc)
|
||||
[](http://www.npmtrends.com/eslint-plugin-jsonc)
|
||||
[](http://www.npmtrends.com/eslint-plugin-jsonc)
|
||||
[](http://www.npmtrends.com/eslint-plugin-jsonc)
|
||||
[](http://www.npmtrends.com/eslint-plugin-jsonc)
|
||||
[](http://www.npmtrends.com/eslint-plugin-jsonc)
|
||||
[](https://github.com/ota-meshi/eslint-plugin-jsonc/actions?query=workflow%3ACI)
|
||||
[](https://coveralls.io/github/ota-meshi/eslint-plugin-jsonc?branch=master)
|
||||
|
||||
## :name_badge: Features
|
||||
|
||||
This ESLint plugin provides linting rules relate to better ways to help you avoid problems when using [JSON], [JSONC] and [JSON5].
|
||||
|
||||
- You can use ESLint to lint [JSON].
|
||||
- You can apply rules similar to the rules you use for JavaScript to JSON using the [`"jsonc/auto"`](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/auto.html) rule provided by this plugin.
|
||||
- You can choose the appropriate config provided by this plugin depending on whether you are using [JSON], [JSONC] or [JSON5].
|
||||
- Supports [Vue SFC](https://vue-loader.vuejs.org/spec.html) custom blocks such as `<i18n>`.
|
||||
Requirements `vue-eslint-parser` v7.3.0 and above.
|
||||
- Supports ESLint directives. e.g. `// eslint-disable-next-line`
|
||||
- You can check your code in real-time using the ESLint editor integrations.
|
||||
|
||||
You can check on the [Online DEMO](https://ota-meshi.github.io/eslint-plugin-jsonc/playground/).
|
||||
|
||||
## :question: Why is it ESLint plugin?
|
||||
|
||||
ESLint is a great linter for JavaScript.
|
||||
Since [JSON] is a subset of JavaScript, the same parser and rules can be applied to [JSON].
|
||||
Also, [JSONC] and [JSON5], which are variants of [JSON], are more similar to JavaScript than [JSON]. Applying a JavaScript linter to [JSON] is more rational than creating a JSON-specific linter.
|
||||
|
||||
### How does `eslint-plugin-jsonc` work?
|
||||
|
||||
This plugin parses `.json` with its own parser, but this parser just converts AST parsed by `acorn` (It is used internally by the ESLint standard parser) into AST with another name. However, ASTs that do not exist in [JSON] and the superset of JSON syntaxes are reported as parsing errors. By converting the AST to another name, we prevent false positives from ESLint core rules.
|
||||
Moreover, You can do the same linting using the extended rules of the ESLint core rules provided by this plugin.
|
||||
|
||||
The parser package used by this plugin is [jsonc-eslint-parser].
|
||||
|
||||
## :question: How is it different from other JSON plugins?
|
||||
|
||||
### [`@eslint/json`]
|
||||
|
||||
They work similarly, but [`@eslint/json`] is an ESLint JSON language plugin, but `eslint-plugin-jsonc` is an ESLint plugin.
|
||||
|
||||
- `eslint-plugin-jsonc` was created 4 years before [`@eslint/json`] and it has more rules than [`@eslint/json`].
|
||||
- The parser used by `eslint-plugin-jsonc` accepts more non-standard JSON syntax than [`@eslint/json`]. `eslint-plugin-jsonc` has rules that can auto-fixed these non-standard syntax to standard syntax.
|
||||
- `eslint-plugin-jsonc` can also be used together with [`@eslint/json`].
|
||||
|
||||
### Plugins that do not use AST
|
||||
|
||||
e.g. [eslint-plugin-json](https://www.npmjs.com/package/eslint-plugin-json)
|
||||
|
||||
These plugins use the processor to parse and return the results independently, without providing the ESLint engine with AST and source code text.
|
||||
|
||||
Plugins don't provide AST, so you can't use directive comments (e.g. `/* eslint-disable */`).
|
||||
Plugins don't provide source code text, so you can't use it with plugins and rules that use text (e.g. [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), [eol-last](https://eslint.org/docs/rules/eol-last)).
|
||||
Also, most plugins don't support JSON5.
|
||||
|
||||
**eslint-plugin-jsonc** works by providing AST and source code text to ESLint.
|
||||
|
||||
### Plugins that use the same AST as JavaScript
|
||||
|
||||
e.g. [eslint-plugin-json-files](https://www.npmjs.com/package/eslint-plugin-json-files), [eslint-plugin-json-es](https://www.npmjs.com/package/eslint-plugin-json-es)
|
||||
|
||||
These plugins use the same AST as JavaScript for linting.
|
||||
|
||||
Since the plugin uses the same AST as JavaScript, it may not report syntax that is not available in JSON (e.g. `1 + 1`, `(42)`). Also, ESLint core rules and other plugin rules can false positives (e.g. [quote-props](https://eslint.org/docs/rules/quote-props) rule reports quote on keys), which can complicate the your configuration.
|
||||
|
||||
The AST used by **eslint-plugin-jsonc** is similar to JavaScript AST, but with a different node name. This will prevent false positives. This means that it can be easily used in combination with other plugins.
|
||||
|
||||
<!--DOCS_IGNORE_START-->
|
||||
|
||||
## :book: Documentation
|
||||
|
||||
See [documents](https://ota-meshi.github.io/eslint-plugin-jsonc/).
|
||||
|
||||
## :cd: Installation
|
||||
|
||||
```bash
|
||||
npm install --save-dev eslint eslint-plugin-jsonc
|
||||
```
|
||||
|
||||
> **Requirements**
|
||||
>
|
||||
> - ESLint v9.38.0 and above
|
||||
> - Node.js v20.x (>=20.19.0), v22.x (>=22.13.0), v24.x and above
|
||||
|
||||
<!--DOCS_IGNORE_END-->
|
||||
|
||||
## :book: Usage
|
||||
|
||||
<!--USAGE_SECTION_START-->
|
||||
<!--USAGE_GUIDE_START-->
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Configuration (`eslint.config.js`)
|
||||
|
||||
Use `eslint.config.js` file to configure rules. See also: <https://eslint.org/docs/latest/use/configure/configuration-files-new>.
|
||||
|
||||
Example **eslint.config.js**:
|
||||
|
||||
```js
|
||||
import eslintPluginJsonc from 'eslint-plugin-jsonc';
|
||||
export default [
|
||||
// add more generic rule sets here, such as:
|
||||
// js.configs.recommended,
|
||||
...eslintPluginJsonc.configs['recommended-with-jsonc'],
|
||||
{
|
||||
rules: {
|
||||
// override/add rules settings here, such as:
|
||||
// 'jsonc/rule-name': 'error'
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
This plugin provides configs:
|
||||
|
||||
- `*.configs.base` ... Configuration to enable correct JSON parsing.
|
||||
- `*.configs['recommended-with-json']` ... Recommended configuration for JSON.
|
||||
- `*.configs['recommended-with-jsonc']` ... Recommended configuration for JSONC.
|
||||
- `*.configs['recommended-with-json5']` ... Recommended configuration for JSON5.
|
||||
- `*.configs.prettier` ... Turn off rules that may conflict with [Prettier](https://prettier.io/).
|
||||
- `*.configs.all` ... Enables all rules. It's meant for testing, not for production use because it changes with every minor and major version of the plugin. Use it at your own risk.
|
||||
|
||||
For backward compatibility, the `flat/*` prefix is still supported:
|
||||
|
||||
- `*.configs['flat/base']`, `*.configs['flat/recommended-with-json']`, etc.
|
||||
|
||||
This plugin will parse `.json`, `.jsonc` and `.json5` by default using the configuration provided by the plugin (unless you already have a parser configured - see below).
|
||||
|
||||
See [the rule list](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/) to get the `rules` that this plugin provides.
|
||||
|
||||
#### Languages
|
||||
|
||||
This plugin provides the following language identifiers for use in ESLint configurations:
|
||||
|
||||
- `jsonc/json` ... JSON files
|
||||
- `jsonc/jsonc` ... JSONC files
|
||||
- `jsonc/json5` ... JSON5 files
|
||||
- `jsonc/x` ... Extended JSON files that accept any syntax representing static values parseable by [jsonc-eslint-parser](https://github.com/ota-meshi/jsonc-eslint-parser). Recommended because it allows flexible parsing while strict syntax checks can be enforced and auto-fixed using the plugin's rules.
|
||||
|
||||
For example, to apply settings specifically to JSON files, you can use the `language` field in your ESLint configuration:
|
||||
|
||||
```js
|
||||
import eslintPluginJsonc from 'eslint-plugin-jsonc';
|
||||
export default [
|
||||
{
|
||||
files: ["*.json", "**/*.json"],
|
||||
plugins: {
|
||||
jsonc: eslintPluginJsonc,
|
||||
},
|
||||
language: "jsonc/x",
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The configuration above is included in the shareable configs provided by this plugin, so using `configs` is generally recommended.
|
||||
|
||||
See also <https://eslint.org/docs/latest/use/configure/plugins#specify-a-language>
|
||||
|
||||
#### **Experimental** support for `@eslint/json`
|
||||
|
||||
We've launched experimental support for [`@eslint/json`].
|
||||
|
||||
Configure it as follows:
|
||||
|
||||
```js
|
||||
import json from "@eslint/json";
|
||||
import jsonc from 'eslint-plugin-jsonc';
|
||||
|
||||
export default [
|
||||
{
|
||||
plugins: {
|
||||
json,
|
||||
jsonc
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.json"],
|
||||
language: "json/json",
|
||||
rules: {
|
||||
// 'json/rule-name': 'error',
|
||||
// 'jsonc/rule-name': 'error'
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.jsonc", ".vscode/*.json"],
|
||||
language: "json/jsonc",
|
||||
rules: {
|
||||
// 'json/rule-name': 'error',
|
||||
// 'jsonc/rule-name': 'error'
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.json5"],
|
||||
language: "json/json5",
|
||||
rules: {
|
||||
// 'json/rule-name': 'error',
|
||||
// 'jsonc/rule-name': 'error'
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
However, we're not yet sure how best to make this work.
|
||||
Please note that we may change behavior without notice.
|
||||
|
||||
[`@eslint/json`]: https://github.com/eslint/json
|
||||
|
||||
## :computer: Editor Integrations
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
|
||||
|
||||
You have to configure the `eslint.validate` option of the extension to check `.json` files, because the extension targets only `*.js` or `*.jsx` files by default.
|
||||
|
||||
Example **.vscode/settings.json**:
|
||||
|
||||
```json
|
||||
{
|
||||
"eslint.validate": ["javascript", "javascriptreact", "json", "jsonc", "json5"]
|
||||
}
|
||||
```
|
||||
|
||||
<!--USAGE_GUIDE_END-->
|
||||
<!--USAGE_SECTION_END-->
|
||||
|
||||
## :white_check_mark: Rules
|
||||
|
||||
<!--RULES_SECTION_START-->
|
||||
|
||||
The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) automatically fixes problems reported by rules which have a wrench :wrench: below.
|
||||
The rules with the following star :star: are included in the config.
|
||||
|
||||
<!--RULES_TABLE_START-->
|
||||
|
||||
### JSONC Rules
|
||||
|
||||
| Rule ID | Description | Fixable | JSON | JSONC | JSON5 |
|
||||
|:--------|:------------|:-------:|:----:|:-----:|:-----:|
|
||||
| [jsonc/auto](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/auto.html) | apply jsonc rules similar to your configured ESLint core rules | :wrench: | | | |
|
||||
| [jsonc/key-name-casing](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/key-name-casing.html) | enforce naming convention to property key names | | | | |
|
||||
| [jsonc/no-bigint-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-bigint-literals.html) | disallow BigInt literals | | :star: | :star: | :star: |
|
||||
| [jsonc/no-binary-expression](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-binary-expression.html) | disallow binary expression | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-binary-numeric-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-binary-numeric-literals.html) | disallow binary numeric literals | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-comments](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-comments.html) | disallow comments | | :star: | | |
|
||||
| [jsonc/no-escape-sequence-in-identifier](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-escape-sequence-in-identifier.html) | disallow escape sequences in identifiers. | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-hexadecimal-numeric-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-hexadecimal-numeric-literals.html) | disallow hexadecimal numeric literals | :wrench: | :star: | :star: | |
|
||||
| [jsonc/no-infinity](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-infinity.html) | disallow Infinity | | :star: | :star: | |
|
||||
| [jsonc/no-nan](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-nan.html) | disallow NaN | | :star: | :star: | |
|
||||
| [jsonc/no-number-props](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-number-props.html) | disallow number property keys | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-numeric-separators](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-numeric-separators.html) | disallow numeric separators | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-octal-numeric-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-octal-numeric-literals.html) | disallow octal numeric literals | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-parenthesized](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-parenthesized.html) | disallow parentheses around the expression | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-plus-sign](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-plus-sign.html) | disallow plus sign | :wrench: | :star: | :star: | |
|
||||
| [jsonc/no-regexp-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-regexp-literals.html) | disallow RegExp literals | | :star: | :star: | :star: |
|
||||
| [jsonc/no-template-literals](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-template-literals.html) | disallow template literals | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/no-undefined-value](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-undefined-value.html) | disallow `undefined` | | :star: | :star: | :star: |
|
||||
| [jsonc/no-unicode-codepoint-escapes](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-unicode-codepoint-escapes.html) | disallow Unicode code point escape sequences. | :wrench: | :star: | :star: | :star: |
|
||||
| [jsonc/sort-array-values](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/sort-array-values.html) | require array values to be sorted | :wrench: | | | |
|
||||
| [jsonc/sort-keys](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/sort-keys.html) | require object keys to be sorted | :wrench: | | | |
|
||||
| [jsonc/valid-json-number](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/valid-json-number.html) | disallow invalid number for JSON | :wrench: | :star: | :star: | |
|
||||
| [jsonc/vue-custom-block/no-parsing-error](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/vue-custom-block/no-parsing-error.html) | disallow parsing errors in Vue custom blocks | | :star: | :star: | :star: |
|
||||
|
||||
### Extension Rules
|
||||
|
||||
| Rule ID | Description | Fixable | JSON | JSONC | JSON5 |
|
||||
|:--------|:------------|:-------:|:----:|:-----:|:-----:|
|
||||
| [jsonc/array-bracket-newline](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/array-bracket-newline.html) | enforce line breaks after opening and before closing array brackets | :wrench: | | | |
|
||||
| [jsonc/array-bracket-spacing](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/array-bracket-spacing.html) | disallow or enforce spaces inside of brackets | :wrench: | | | |
|
||||
| [jsonc/array-element-newline](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/array-element-newline.html) | enforce line breaks between array elements | :wrench: | | | |
|
||||
| [jsonc/comma-dangle](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/comma-dangle.html) | require or disallow trailing commas | :wrench: | :star: | | |
|
||||
| [jsonc/comma-style](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/comma-style.html) | enforce consistent comma style | :wrench: | | | |
|
||||
| [jsonc/indent](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/indent.html) | enforce consistent indentation | :wrench: | | | |
|
||||
| [jsonc/key-spacing](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/key-spacing.html) | enforce consistent spacing between keys and values in object literal properties | :wrench: | | | |
|
||||
| [jsonc/no-dupe-keys](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-dupe-keys.html) | disallow duplicate keys in object literals | | :star: | :star: | :star: |
|
||||
| [jsonc/no-floating-decimal](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-floating-decimal.html) | disallow leading or trailing decimal points in numeric literals | :wrench: | :star: | :star: | |
|
||||
| [jsonc/no-irregular-whitespace](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-irregular-whitespace.html) | disallow irregular whitespace | | :star: | :star: | :star: |
|
||||
| [jsonc/no-multi-str](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-multi-str.html) | disallow multiline strings | | :star: | :star: | |
|
||||
| [jsonc/no-octal-escape](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-octal-escape.html) | disallow octal escape sequences in string literals | | | | |
|
||||
| [jsonc/no-octal](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-octal.html) | disallow legacy octal literals | | :star: | :star: | :star: |
|
||||
| [jsonc/no-sparse-arrays](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-sparse-arrays.html) | disallow sparse arrays | | :star: | :star: | :star: |
|
||||
| [jsonc/no-useless-escape](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-useless-escape.html) | disallow unnecessary escape usage | | :star: | :star: | :star: |
|
||||
| [jsonc/object-curly-newline](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/object-curly-newline.html) | enforce consistent line breaks inside braces | :wrench: | | | |
|
||||
| [jsonc/object-curly-spacing](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/object-curly-spacing.html) | enforce consistent spacing inside braces | :wrench: | | | |
|
||||
| [jsonc/object-property-newline](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/object-property-newline.html) | enforce placing object properties on separate lines | :wrench: | | | |
|
||||
| [jsonc/quote-props](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/quote-props.html) | require quotes around object literal property names | :wrench: | :star: | :star: | |
|
||||
| [jsonc/quotes](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/quotes.html) | enforce use of double or single quotes | :wrench: | :star: | :star: | |
|
||||
| [jsonc/space-unary-ops](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/space-unary-ops.html) | disallow spaces after unary operators | :wrench: | :star: | :star: | :star: |
|
||||
|
||||
<!--RULES_TABLE_END-->
|
||||
<!--RULES_SECTION_END-->
|
||||
|
||||
## :rocket: To Do More Verification
|
||||
|
||||
### Verify using JSON Schema
|
||||
|
||||
You can verify using JSON Schema by checking and installing [eslint-plugin-json-schema-validator].
|
||||
|
||||
### Verify the [Vue I18n] message resource files
|
||||
|
||||
You can verify the message files by checking and installing [@intlify/eslint-plugin-vue-i18n].
|
||||
|
||||
<!--DOCS_IGNORE_START-->
|
||||
|
||||
## :traffic_light: Semantic Versioning Policy
|
||||
|
||||
**eslint-plugin-jsonc** follows [Semantic Versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy).
|
||||
|
||||
## :beers: Contributing
|
||||
|
||||
Welcome contributing!
|
||||
|
||||
Please use GitHub's Issues/PRs.
|
||||
|
||||
### Development Tools
|
||||
|
||||
- `npm test` runs tests and measures coverage.
|
||||
- `npm run update` runs in order to update readme and recommended configuration.
|
||||
|
||||
<!--DOCS_IGNORE_END-->
|
||||
|
||||
## :couple: Related Packages
|
||||
|
||||
- [eslint-plugin-yml](https://github.com/ota-meshi/eslint-plugin-yml) ... ESLint plugin for YAML.
|
||||
- [eslint-plugin-toml](https://github.com/ota-meshi/eslint-plugin-toml) ... ESLint plugin for TOML.
|
||||
- [eslint-plugin-json-schema-validator](https://github.com/ota-meshi/eslint-plugin-json-schema-validator) ... ESLint plugin that validates data using JSON Schema Validator.
|
||||
- [jsonc-eslint-parser](https://github.com/ota-meshi/jsonc-eslint-parser) ... JSON, JSONC and JSON5 parser for use with ESLint plugins.
|
||||
- [yaml-eslint-parser](https://github.com/ota-meshi/yaml-eslint-parser) ... YAML parser for use with ESLint plugins.
|
||||
- [toml-eslint-parser](https://github.com/ota-meshi/toml-eslint-parser) ... TOML parser for use with ESLint plugins.
|
||||
|
||||
## :lock: License
|
||||
|
||||
See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
|
||||
|
||||
[json]: https://json.org/
|
||||
[jsonc]: https://github.com/microsoft/node-jsonc-parser
|
||||
[json5]: https://json5.org/
|
||||
[jsonc-eslint-parser]: https://github.com/ota-meshi/jsonc-eslint-parser
|
||||
[eslint-plugin-json-schema-validator]: https://github.com/ota-meshi/eslint-plugin-json-schema-validator
|
||||
[@intlify/eslint-plugin-vue-i18n]: https://github.com/intlify/eslint-plugin-vue-i18n
|
||||
[vue i18n]: https://github.com/intlify/vue-i18n-next
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
//#region \0rolldown/runtime.js
|
||||
var __defProp = Object.defineProperty;
|
||||
var __exportAll = (all, no_symbols) => {
|
||||
let target = {};
|
||||
for (var name in all) {
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
if (!no_symbols) {
|
||||
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { __exportAll as t };
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export { };
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { runAsWorker } from "synckit";
|
||||
import { ESLint } from "eslint";
|
||||
|
||||
//#region lib/utils/get-auto-jsonc-rules-config/get-auto-jsonc-rules-config-worker.ts
|
||||
runAsWorker(async (cwd, fileName) => {
|
||||
return { rules: (await new ESLint({ cwd }).calculateConfigForFile(fileName)).rules };
|
||||
});
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { a as JSONCSourceCode, i as JSONCComment, n as JSONCLanguage, o as JSONCToken, r as JSONCLanguageOptions, t as RuleModule } from "./types-DS229oMx.mjs";
|
||||
import { Linter } from "eslint";
|
||||
|
||||
//#region lib/meta.d.ts
|
||||
declare namespace meta_d_exports {
|
||||
export { name, version };
|
||||
}
|
||||
declare const name: "eslint-plugin-jsonc";
|
||||
declare const version: "3.1.2";
|
||||
//#endregion
|
||||
//#region lib/index.d.ts
|
||||
declare const configs: {
|
||||
base: Linter.Config[];
|
||||
"recommended-with-json": Linter.Config[];
|
||||
"recommended-with-jsonc": Linter.Config[];
|
||||
"recommended-with-json5": Linter.Config[];
|
||||
prettier: Linter.Config[];
|
||||
all: Linter.Config[];
|
||||
"flat/base": Linter.Config[];
|
||||
"flat/recommended-with-json": Linter.Config[];
|
||||
"flat/recommended-with-jsonc": Linter.Config[];
|
||||
"flat/recommended-with-json5": Linter.Config[];
|
||||
"flat/prettier": Linter.Config[];
|
||||
"flat/all": Linter.Config[];
|
||||
};
|
||||
declare const rules: {
|
||||
[key: string]: RuleModule<unknown[]>;
|
||||
};
|
||||
declare const languages: {
|
||||
json: JSONCLanguage;
|
||||
jsonc: JSONCLanguage;
|
||||
json5: JSONCLanguage;
|
||||
x: JSONCLanguage;
|
||||
};
|
||||
declare const _default: {
|
||||
meta: typeof meta_d_exports;
|
||||
configs: {
|
||||
base: Linter.Config[];
|
||||
"recommended-with-json": Linter.Config[];
|
||||
"recommended-with-jsonc": Linter.Config[];
|
||||
"recommended-with-json5": Linter.Config[];
|
||||
prettier: Linter.Config[];
|
||||
all: Linter.Config[];
|
||||
"flat/base": Linter.Config[];
|
||||
"flat/recommended-with-json": Linter.Config[];
|
||||
"flat/recommended-with-jsonc": Linter.Config[];
|
||||
"flat/recommended-with-json5": Linter.Config[];
|
||||
"flat/prettier": Linter.Config[];
|
||||
"flat/all": Linter.Config[];
|
||||
};
|
||||
rules: {
|
||||
[key: string]: RuleModule<unknown[]>;
|
||||
};
|
||||
languages: {
|
||||
json: JSONCLanguage;
|
||||
jsonc: JSONCLanguage;
|
||||
json5: JSONCLanguage;
|
||||
x: JSONCLanguage;
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
export { type JSONCComment, type JSONCLanguageOptions, type JSONCSourceCode, type JSONCToken, configs, _default as default, languages, meta_d_exports as meta, rules };
|
||||
+587
@@ -0,0 +1,587 @@
|
||||
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
||||
import "./no-parsing-error-B9_Ixkn3.mjs";
|
||||
import { getRules } from "./rules.mjs";
|
||||
import { VisitorKeys, parseForESLint, traverseNodes } from "jsonc-eslint-parser";
|
||||
import { CallMethodStep, ConfigCommentParser, Directive, TextSourceCodeBase, VisitNodeStep } from "@eslint/plugin-kit";
|
||||
import { TokenStore } from "@ota-meshi/ast-token-store";
|
||||
|
||||
//#region lib/configs/flat/base.ts
|
||||
var base_default = [{ plugins: { get jsonc() {
|
||||
return lib_default;
|
||||
} } }, {
|
||||
files: [
|
||||
"*.json",
|
||||
"**/*.json",
|
||||
"*.json5",
|
||||
"**/*.json5",
|
||||
"*.jsonc",
|
||||
"**/*.jsonc"
|
||||
],
|
||||
language: "jsonc/x",
|
||||
rules: {
|
||||
strict: "off",
|
||||
"no-unused-expressions": "off",
|
||||
"no-unused-vars": "off"
|
||||
}
|
||||
}];
|
||||
|
||||
//#endregion
|
||||
//#region lib/configs/flat/recommended-with-json.ts
|
||||
var recommended_with_json_default = [...base_default, { rules: {
|
||||
"jsonc/comma-dangle": "error",
|
||||
"jsonc/no-bigint-literals": "error",
|
||||
"jsonc/no-binary-expression": "error",
|
||||
"jsonc/no-binary-numeric-literals": "error",
|
||||
"jsonc/no-comments": "error",
|
||||
"jsonc/no-dupe-keys": "error",
|
||||
"jsonc/no-escape-sequence-in-identifier": "error",
|
||||
"jsonc/no-floating-decimal": "error",
|
||||
"jsonc/no-hexadecimal-numeric-literals": "error",
|
||||
"jsonc/no-infinity": "error",
|
||||
"jsonc/no-irregular-whitespace": "error",
|
||||
"jsonc/no-multi-str": "error",
|
||||
"jsonc/no-nan": "error",
|
||||
"jsonc/no-number-props": "error",
|
||||
"jsonc/no-numeric-separators": "error",
|
||||
"jsonc/no-octal-numeric-literals": "error",
|
||||
"jsonc/no-octal": "error",
|
||||
"jsonc/no-parenthesized": "error",
|
||||
"jsonc/no-plus-sign": "error",
|
||||
"jsonc/no-regexp-literals": "error",
|
||||
"jsonc/no-sparse-arrays": "error",
|
||||
"jsonc/no-template-literals": "error",
|
||||
"jsonc/no-undefined-value": "error",
|
||||
"jsonc/no-unicode-codepoint-escapes": "error",
|
||||
"jsonc/no-useless-escape": "error",
|
||||
"jsonc/quote-props": "error",
|
||||
"jsonc/quotes": "error",
|
||||
"jsonc/space-unary-ops": "error",
|
||||
"jsonc/valid-json-number": "error",
|
||||
"jsonc/vue-custom-block/no-parsing-error": "error"
|
||||
} }];
|
||||
|
||||
//#endregion
|
||||
//#region lib/configs/flat/recommended-with-jsonc.ts
|
||||
var recommended_with_jsonc_default = [...base_default, { rules: {
|
||||
"jsonc/no-bigint-literals": "error",
|
||||
"jsonc/no-binary-expression": "error",
|
||||
"jsonc/no-binary-numeric-literals": "error",
|
||||
"jsonc/no-dupe-keys": "error",
|
||||
"jsonc/no-escape-sequence-in-identifier": "error",
|
||||
"jsonc/no-floating-decimal": "error",
|
||||
"jsonc/no-hexadecimal-numeric-literals": "error",
|
||||
"jsonc/no-infinity": "error",
|
||||
"jsonc/no-irregular-whitespace": "error",
|
||||
"jsonc/no-multi-str": "error",
|
||||
"jsonc/no-nan": "error",
|
||||
"jsonc/no-number-props": "error",
|
||||
"jsonc/no-numeric-separators": "error",
|
||||
"jsonc/no-octal-numeric-literals": "error",
|
||||
"jsonc/no-octal": "error",
|
||||
"jsonc/no-parenthesized": "error",
|
||||
"jsonc/no-plus-sign": "error",
|
||||
"jsonc/no-regexp-literals": "error",
|
||||
"jsonc/no-sparse-arrays": "error",
|
||||
"jsonc/no-template-literals": "error",
|
||||
"jsonc/no-undefined-value": "error",
|
||||
"jsonc/no-unicode-codepoint-escapes": "error",
|
||||
"jsonc/no-useless-escape": "error",
|
||||
"jsonc/quote-props": "error",
|
||||
"jsonc/quotes": "error",
|
||||
"jsonc/space-unary-ops": "error",
|
||||
"jsonc/valid-json-number": "error",
|
||||
"jsonc/vue-custom-block/no-parsing-error": "error"
|
||||
} }];
|
||||
|
||||
//#endregion
|
||||
//#region lib/configs/flat/recommended-with-json5.ts
|
||||
var recommended_with_json5_default = [...base_default, { rules: {
|
||||
"jsonc/no-bigint-literals": "error",
|
||||
"jsonc/no-binary-expression": "error",
|
||||
"jsonc/no-binary-numeric-literals": "error",
|
||||
"jsonc/no-dupe-keys": "error",
|
||||
"jsonc/no-escape-sequence-in-identifier": "error",
|
||||
"jsonc/no-irregular-whitespace": "error",
|
||||
"jsonc/no-number-props": "error",
|
||||
"jsonc/no-numeric-separators": "error",
|
||||
"jsonc/no-octal-numeric-literals": "error",
|
||||
"jsonc/no-octal": "error",
|
||||
"jsonc/no-parenthesized": "error",
|
||||
"jsonc/no-regexp-literals": "error",
|
||||
"jsonc/no-sparse-arrays": "error",
|
||||
"jsonc/no-template-literals": "error",
|
||||
"jsonc/no-undefined-value": "error",
|
||||
"jsonc/no-unicode-codepoint-escapes": "error",
|
||||
"jsonc/no-useless-escape": "error",
|
||||
"jsonc/space-unary-ops": "error",
|
||||
"jsonc/vue-custom-block/no-parsing-error": "error"
|
||||
} }];
|
||||
|
||||
//#endregion
|
||||
//#region lib/configs/flat/prettier.ts
|
||||
var prettier_default = [...base_default, { rules: {
|
||||
"jsonc/array-bracket-newline": "off",
|
||||
"jsonc/array-bracket-spacing": "off",
|
||||
"jsonc/array-element-newline": "off",
|
||||
"jsonc/comma-dangle": "off",
|
||||
"jsonc/comma-style": "off",
|
||||
"jsonc/indent": "off",
|
||||
"jsonc/key-spacing": "off",
|
||||
"jsonc/no-floating-decimal": "off",
|
||||
"jsonc/object-curly-newline": "off",
|
||||
"jsonc/object-curly-spacing": "off",
|
||||
"jsonc/object-property-newline": "off",
|
||||
"jsonc/quote-props": "off",
|
||||
"jsonc/quotes": "off",
|
||||
"jsonc/space-unary-ops": "off"
|
||||
} }];
|
||||
|
||||
//#endregion
|
||||
//#region lib/configs/flat/all.ts
|
||||
const all = {};
|
||||
for (const rule of getRules()) {
|
||||
if (rule.meta.docs.ruleId === "jsonc/sort-array-values") continue;
|
||||
all[rule.meta.docs.ruleId] = "error";
|
||||
}
|
||||
const config = [...base_default, { rules: { ...all } }];
|
||||
|
||||
//#endregion
|
||||
//#region lib/meta.ts
|
||||
var meta_exports = /* @__PURE__ */ __exportAll({
|
||||
name: () => name,
|
||||
version: () => version
|
||||
});
|
||||
const name = "eslint-plugin-jsonc";
|
||||
const version = "3.1.2";
|
||||
|
||||
//#endregion
|
||||
//#region lib/language/jsonc-source-code.ts
|
||||
/**
|
||||
* @fileoverview The JSONCSourceCode class.
|
||||
*/
|
||||
const commentParser = new ConfigCommentParser();
|
||||
/**
|
||||
* Pattern to match ESLint inline configuration comments in JSONC.
|
||||
* Matches: eslint, eslint-disable, eslint-enable, eslint-disable-line, eslint-disable-next-line
|
||||
*/
|
||||
const INLINE_CONFIG = /^\s*eslint(?:-enable|-disable(?:(?:-next)?-line)?)?(?:\s|$)/u;
|
||||
/**
|
||||
* JSONC Source Code Object
|
||||
*/
|
||||
var JSONCSourceCode = class extends TextSourceCodeBase {
|
||||
hasBOM;
|
||||
parserServices;
|
||||
visitorKeys;
|
||||
tokenStore;
|
||||
#steps = null;
|
||||
#cacheTokensAndComments = null;
|
||||
#inlineConfigComments = null;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
constructor(config) {
|
||||
super({
|
||||
ast: config.ast,
|
||||
text: config.text
|
||||
});
|
||||
this.hasBOM = Boolean(config.hasBOM);
|
||||
this.parserServices = config.parserServices;
|
||||
this.visitorKeys = config.visitorKeys || VisitorKeys;
|
||||
this.tokenStore = new TokenStore({
|
||||
tokens: [...config.ast.tokens, ...config.ast.comments],
|
||||
isComment: (token) => token.type === "Block" || token.type === "Line"
|
||||
});
|
||||
}
|
||||
traverse() {
|
||||
if (this.#steps != null) return this.#steps;
|
||||
const steps = [];
|
||||
this.#steps = steps;
|
||||
const root = this.ast;
|
||||
steps.push(new CallMethodStep({
|
||||
target: "onCodePathStart",
|
||||
args: [{}, root]
|
||||
}));
|
||||
traverseNodes(root, {
|
||||
enterNode(n) {
|
||||
steps.push(new VisitNodeStep({
|
||||
target: n,
|
||||
phase: 1,
|
||||
args: [n]
|
||||
}));
|
||||
},
|
||||
leaveNode(n) {
|
||||
steps.push(new VisitNodeStep({
|
||||
target: n,
|
||||
phase: 2,
|
||||
args: [n]
|
||||
}));
|
||||
}
|
||||
});
|
||||
steps.push(new CallMethodStep({
|
||||
target: "onCodePathEnd",
|
||||
args: [{}, root]
|
||||
}));
|
||||
return steps;
|
||||
}
|
||||
/**
|
||||
* Gets all tokens and comments.
|
||||
*/
|
||||
get tokensAndComments() {
|
||||
return this.#cacheTokensAndComments ??= [...this.ast.tokens, ...this.ast.comments].sort((a, b) => a.range[0] - b.range[0]);
|
||||
}
|
||||
getLines() {
|
||||
return this.lines;
|
||||
}
|
||||
getAllComments() {
|
||||
return this.tokenStore.getAllComments();
|
||||
}
|
||||
/**
|
||||
* Returns an array of all inline configuration nodes found in the source code.
|
||||
* This includes eslint-disable, eslint-enable, eslint-disable-line,
|
||||
* eslint-disable-next-line, and eslint (for inline config) comments.
|
||||
*/
|
||||
getInlineConfigNodes() {
|
||||
if (!this.#inlineConfigComments) this.#inlineConfigComments = this.ast.comments.filter((comment) => INLINE_CONFIG.test(comment.value));
|
||||
return this.#inlineConfigComments;
|
||||
}
|
||||
/**
|
||||
* Returns directives that enable or disable rules along with any problems
|
||||
* encountered while parsing the directives.
|
||||
*/
|
||||
getDisableDirectives() {
|
||||
const problems = [];
|
||||
const directives = [];
|
||||
this.getInlineConfigNodes().forEach((comment) => {
|
||||
const directive = commentParser.parseDirective(comment.value);
|
||||
if (!directive) return;
|
||||
const { label, value, justification } = directive;
|
||||
if (label === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
||||
const message = `${label} comment should not span multiple lines.`;
|
||||
problems.push({
|
||||
ruleId: null,
|
||||
message,
|
||||
loc: comment.loc
|
||||
});
|
||||
return;
|
||||
}
|
||||
switch (label) {
|
||||
case "eslint-disable":
|
||||
case "eslint-enable":
|
||||
case "eslint-disable-next-line":
|
||||
case "eslint-disable-line": {
|
||||
const directiveType = label.slice(7);
|
||||
directives.push(new Directive({
|
||||
type: directiveType,
|
||||
node: comment,
|
||||
value,
|
||||
justification
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
problems,
|
||||
directives
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Returns inline rule configurations along with any problems
|
||||
* encountered while parsing the configurations.
|
||||
*/
|
||||
applyInlineConfig() {
|
||||
const problems = [];
|
||||
const configs = [];
|
||||
this.getInlineConfigNodes().forEach((comment) => {
|
||||
const directive = commentParser.parseDirective(comment.value);
|
||||
if (!directive) return;
|
||||
const { label, value } = directive;
|
||||
if (label === "eslint") {
|
||||
const parseResult = commentParser.parseJSONLikeConfig(value);
|
||||
if (parseResult.ok) configs.push({
|
||||
config: { rules: parseResult.config },
|
||||
loc: comment.loc
|
||||
});
|
||||
else problems.push({
|
||||
ruleId: null,
|
||||
message: parseResult.error.message,
|
||||
loc: comment.loc
|
||||
});
|
||||
}
|
||||
});
|
||||
return {
|
||||
configs,
|
||||
problems
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Gets the source text for the given node or the entire source if no node is provided.
|
||||
*/
|
||||
getText(node, beforeCount, afterCount) {
|
||||
if (!node) return this.text;
|
||||
const range = node.range;
|
||||
const start = range[0] - (beforeCount ?? 0);
|
||||
const end = range[1] + (afterCount ?? 0);
|
||||
return this.text.slice(Math.max(0, start), Math.min(this.text.length, end));
|
||||
}
|
||||
getNodeByRangeIndex(index) {
|
||||
let node = find([this.ast]);
|
||||
if (!node) return null;
|
||||
while (true) {
|
||||
const child = find(this._getChildren(node));
|
||||
if (!child) return node;
|
||||
node = child;
|
||||
}
|
||||
/**
|
||||
* Finds a node that contains the given index.
|
||||
*/
|
||||
function find(nodes) {
|
||||
for (const node of nodes) if (node.range[0] <= index && index < node.range[1]) return node;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
getFirstToken(node, options) {
|
||||
return this.tokenStore.getFirstToken(node, options);
|
||||
}
|
||||
getFirstTokens(node, options) {
|
||||
return this.tokenStore.getFirstTokens(node, options);
|
||||
}
|
||||
getLastToken(node, options) {
|
||||
return this.tokenStore.getLastToken(node, options);
|
||||
}
|
||||
getLastTokens(node, options) {
|
||||
return this.tokenStore.getLastTokens(node, options);
|
||||
}
|
||||
getTokenBefore(node, options) {
|
||||
return this.tokenStore.getTokenBefore(node, options);
|
||||
}
|
||||
getTokensBefore(node, options) {
|
||||
return this.tokenStore.getTokensBefore(node, options);
|
||||
}
|
||||
getTokenAfter(node, options) {
|
||||
return this.tokenStore.getTokenAfter(node, options);
|
||||
}
|
||||
getTokensAfter(node, options) {
|
||||
return this.tokenStore.getTokensAfter(node, options);
|
||||
}
|
||||
getFirstTokenBetween(left, right, options) {
|
||||
return this.tokenStore.getFirstTokenBetween(left, right, options);
|
||||
}
|
||||
getFirstTokensBetween(left, right, options) {
|
||||
return this.tokenStore.getFirstTokensBetween(left, right, options);
|
||||
}
|
||||
getLastTokenBetween(left, right, options) {
|
||||
return this.tokenStore.getLastTokenBetween(left, right, options);
|
||||
}
|
||||
getLastTokensBetween(left, right, options) {
|
||||
return this.tokenStore.getLastTokensBetween(left, right, options);
|
||||
}
|
||||
getTokens(node, options) {
|
||||
return this.tokenStore.getTokens(node, options);
|
||||
}
|
||||
getTokensBetween(left, right, options) {
|
||||
return this.tokenStore.getTokensBetween(left, right, options);
|
||||
}
|
||||
getCommentsInside(nodeOrToken) {
|
||||
return this.tokenStore.getCommentsInside(nodeOrToken);
|
||||
}
|
||||
getCommentsBefore(nodeOrToken) {
|
||||
return this.tokenStore.getCommentsBefore(nodeOrToken);
|
||||
}
|
||||
getCommentsAfter(nodeOrToken) {
|
||||
return this.tokenStore.getCommentsAfter(nodeOrToken);
|
||||
}
|
||||
commentsExistBetween(first, second) {
|
||||
return this.tokenStore.commentsExistBetween(first, second);
|
||||
}
|
||||
isSpaceBetween(first, second) {
|
||||
const [left, right] = first.range[1] <= second.range[0] ? [first, second] : [second, first];
|
||||
return this.tokenStore.isSpaceBetween(left, right);
|
||||
}
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated JSONC does not have scopes
|
||||
*/
|
||||
getScope(node) {
|
||||
if (node?.type !== "Program") return null;
|
||||
return createFakeGlobalScope(this.ast);
|
||||
}
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated JSONC does not have scopes
|
||||
*/
|
||||
get scopeManager() {
|
||||
return {
|
||||
scopes: [],
|
||||
globalScope: createFakeGlobalScope(this.ast),
|
||||
acquire: (node) => {
|
||||
if (node.type === "Program") return createFakeGlobalScope(this.ast);
|
||||
return null;
|
||||
},
|
||||
getDeclaredVariables: () => [],
|
||||
addGlobals: () => {}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated
|
||||
*/
|
||||
isSpaceBetweenTokens(first, second) {
|
||||
return this.isSpaceBetween(first, second);
|
||||
}
|
||||
_getChildren(node) {
|
||||
const keys = this.visitorKeys[node.type] || [];
|
||||
const children = [];
|
||||
for (const key of keys) {
|
||||
const value = node[key];
|
||||
if (Array.isArray(value)) {
|
||||
for (const element of value) if (isNode(element)) children.push(element);
|
||||
} else if (isNode(value)) children.push(value);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Determines whether the given value is a JSONC AST node.
|
||||
*/
|
||||
function isNode(value) {
|
||||
return typeof value === "object" && value !== null && typeof value.type === "string" && Array.isArray(value.range) && Boolean(value.loc) && typeof value.loc === "object";
|
||||
}
|
||||
/**
|
||||
* Creates a fake global scope for JSONC files.
|
||||
* @deprecated JSONC does not have scopes
|
||||
*/
|
||||
function createFakeGlobalScope(node) {
|
||||
const fakeGlobalScope = {
|
||||
type: "global",
|
||||
block: node,
|
||||
set: /* @__PURE__ */ new Map(),
|
||||
through: [],
|
||||
childScopes: [],
|
||||
variableScope: null,
|
||||
variables: [],
|
||||
references: [],
|
||||
functionExpressionScope: false,
|
||||
isStrict: false,
|
||||
upper: null,
|
||||
implicit: {
|
||||
variables: [],
|
||||
set: /* @__PURE__ */ new Map()
|
||||
}
|
||||
};
|
||||
fakeGlobalScope.variableScope = fakeGlobalScope;
|
||||
return fakeGlobalScope;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region lib/language/jsonc-language.ts
|
||||
/**
|
||||
* The JSONC language implementation for ESLint.
|
||||
*/
|
||||
var JSONCLanguage = class {
|
||||
/**
|
||||
* The type of file to read.
|
||||
*/
|
||||
fileType = "text";
|
||||
/**
|
||||
* The line number at which the parser starts counting.
|
||||
*/
|
||||
lineStart = 1;
|
||||
/**
|
||||
* The column number at which the parser starts counting.
|
||||
*/
|
||||
columnStart = 0;
|
||||
/**
|
||||
* The name of the key that holds the type of the node.
|
||||
*/
|
||||
nodeTypeKey = "type";
|
||||
_mode;
|
||||
constructor(options) {
|
||||
this._mode = options?.mode ?? "EXTENDED";
|
||||
}
|
||||
/**
|
||||
* Validates the language options.
|
||||
*/
|
||||
validateLanguageOptions(_languageOptions) {}
|
||||
normalizeLanguageOptions(languageOptions) {
|
||||
return {
|
||||
ecmaVersion: "latest",
|
||||
...languageOptions,
|
||||
parserOptions: {
|
||||
...this._mode !== "EXTENDED" ? { jsonSyntax: this._mode } : {},
|
||||
...languageOptions.parserOptions
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Parses the given file into an AST.
|
||||
*/
|
||||
parse(file, context) {
|
||||
const text = file.body;
|
||||
try {
|
||||
return {
|
||||
ok: true,
|
||||
ast: parseForESLint(text, { jsonSyntax: context.languageOptions?.parserOptions?.jsonSyntax ?? (this._mode !== "EXTENDED" ? this._mode : void 0) }).ast
|
||||
};
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const parseError = error;
|
||||
return {
|
||||
ok: false,
|
||||
errors: [{
|
||||
message,
|
||||
line: parseError.lineNumber ?? 1,
|
||||
column: parseError.column ?? 1
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a new SourceCode object for the given file and parse result.
|
||||
*/
|
||||
createSourceCode(file, parseResult) {
|
||||
return new JSONCSourceCode({
|
||||
text: file.body,
|
||||
ast: parseResult.ast,
|
||||
hasBOM: file.bom,
|
||||
parserServices: { isJSON: true },
|
||||
visitorKeys: VisitorKeys
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
//#region lib/index.ts
|
||||
const configs = {
|
||||
base: base_default,
|
||||
"recommended-with-json": recommended_with_json_default,
|
||||
"recommended-with-jsonc": recommended_with_jsonc_default,
|
||||
"recommended-with-json5": recommended_with_json5_default,
|
||||
prettier: prettier_default,
|
||||
all: config,
|
||||
"flat/base": base_default,
|
||||
"flat/recommended-with-json": recommended_with_json_default,
|
||||
"flat/recommended-with-jsonc": recommended_with_jsonc_default,
|
||||
"flat/recommended-with-json5": recommended_with_json5_default,
|
||||
"flat/prettier": prettier_default,
|
||||
"flat/all": config
|
||||
};
|
||||
const rules = getRules().reduce((obj, r) => {
|
||||
obj[r.meta.docs.ruleName] = r;
|
||||
return obj;
|
||||
}, {});
|
||||
const languages = {
|
||||
json: new JSONCLanguage({ mode: "JSON" }),
|
||||
jsonc: new JSONCLanguage({ mode: "JSONC" }),
|
||||
json5: new JSONCLanguage({ mode: "JSON5" }),
|
||||
x: new JSONCLanguage({ mode: "EXTENDED" })
|
||||
};
|
||||
var lib_default = {
|
||||
meta: meta_exports,
|
||||
configs,
|
||||
rules,
|
||||
languages
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { configs, lib_default as default, languages, meta_exports as meta, rules };
|
||||
+5954
File diff suppressed because it is too large
Load Diff
+9
@@ -0,0 +1,9 @@
|
||||
import { t as RuleModule } from "./types-DS229oMx.mjs";
|
||||
|
||||
//#region lib/utils/rules.d.ts
|
||||
/**
|
||||
*
|
||||
*/
|
||||
declare function getRules(): RuleModule[];
|
||||
//#endregion
|
||||
export { getRules };
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { A as no_escape_sequence_in_identifier_default, B as comma_dangle_default, C as no_number_props_default, D as no_infinity_default, E as no_irregular_whitespace_default, F as no_bigint_literals_default, H as array_element_newline_default, I as key_spacing_default, L as key_name_casing_default, M as no_comments_default, N as no_binary_numeric_literals_default, O as no_hexadecimal_numeric_literals_default, P as no_binary_expression_default, R as indent_default, S as no_numeric_separators_default, T as no_multi_str_default, U as array_bracket_spacing_default, V as auto_default, W as array_bracket_newline_default, _ as no_plus_sign_default, a as sort_array_values_default, b as no_octal_numeric_literals_default, c as object_property_newline_default, d as no_useless_escape_default, f as no_unicode_codepoint_escapes_default, g as no_regexp_literals_default, h as no_sparse_arrays_default, i as sort_keys_default, j as no_dupe_keys_default, k as no_floating_decimal_default, l as object_curly_spacing_default, m as no_template_literals_default, n as valid_json_number_default, o as quotes_default, p as no_undefined_value_default, r as space_unary_ops_default, s as quote_props_default, t as no_parsing_error_default, u as object_curly_newline_default, v as no_parenthesized_default, w as no_nan_default, x as no_octal_escape_default, y as no_octal_default, z as comma_style_default } from "./no-parsing-error-B9_Ixkn3.mjs";
|
||||
|
||||
//#region lib/utils/rules.ts
|
||||
let rules = null;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function getRules() {
|
||||
if (rules) return rules;
|
||||
rules = [
|
||||
array_bracket_newline_default,
|
||||
array_bracket_spacing_default,
|
||||
array_element_newline_default,
|
||||
auto_default,
|
||||
comma_dangle_default,
|
||||
comma_style_default,
|
||||
indent_default,
|
||||
key_name_casing_default,
|
||||
key_spacing_default,
|
||||
no_bigint_literals_default,
|
||||
no_binary_expression_default,
|
||||
no_binary_numeric_literals_default,
|
||||
no_comments_default,
|
||||
no_dupe_keys_default,
|
||||
no_escape_sequence_in_identifier_default,
|
||||
no_floating_decimal_default,
|
||||
no_hexadecimal_numeric_literals_default,
|
||||
no_infinity_default,
|
||||
no_irregular_whitespace_default,
|
||||
no_multi_str_default,
|
||||
no_nan_default,
|
||||
no_number_props_default,
|
||||
no_numeric_separators_default,
|
||||
no_octal_escape_default,
|
||||
no_octal_numeric_literals_default,
|
||||
no_octal_default,
|
||||
no_parenthesized_default,
|
||||
no_plus_sign_default,
|
||||
no_regexp_literals_default,
|
||||
no_sparse_arrays_default,
|
||||
no_template_literals_default,
|
||||
no_undefined_value_default,
|
||||
no_unicode_codepoint_escapes_default,
|
||||
no_useless_escape_default,
|
||||
object_curly_newline_default,
|
||||
object_curly_spacing_default,
|
||||
object_property_newline_default,
|
||||
quote_props_default,
|
||||
quotes_default,
|
||||
sort_array_values_default,
|
||||
sort_keys_default,
|
||||
space_unary_ops_default,
|
||||
valid_json_number_default,
|
||||
no_parsing_error_default
|
||||
];
|
||||
return rules;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { getRules };
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
import { AST, JSONParserOptions, RuleListener } from "jsonc-eslint-parser";
|
||||
import { IDirective, TextSourceCodeBase, TraversalStep } from "@eslint/plugin-kit";
|
||||
import { CursorWithCountOptionsWithComment, CursorWithCountOptionsWithFilter, CursorWithCountOptionsWithoutFilter, CursorWithSkipOptionsWithComment, CursorWithSkipOptionsWithFilter, CursorWithSkipOptionsWithoutFilter } from "@ota-meshi/ast-token-store";
|
||||
import { AST as AST$1, Scope } from "eslint";
|
||||
import * as core from "@eslint/core";
|
||||
import { File, FileProblem, Language, NotOkParseResult, OkParseResult, RulesConfig } from "@eslint/core";
|
||||
import { Comment } from "estree";
|
||||
|
||||
//#region lib/language/jsonc-source-code.d.ts
|
||||
/**
|
||||
* A comment token with required range and loc.
|
||||
*/
|
||||
type JSONCComment = Comment & {
|
||||
range: [number, number];
|
||||
loc: AST.SourceLocation;
|
||||
};
|
||||
/**
|
||||
* JSONC-specific syntax element type
|
||||
*/
|
||||
type JSONCSyntaxElement = AST.JSONNode | JSONCTokenOrComment;
|
||||
type JSONCToken = AST$1.Token;
|
||||
type JSONCTokenOrComment = JSONCToken | JSONCComment;
|
||||
/**
|
||||
* JSONC Source Code Object
|
||||
*/
|
||||
declare class JSONCSourceCode extends TextSourceCodeBase<{
|
||||
LangOptions: Record<never, never>;
|
||||
RootNode: AST.JSONProgram;
|
||||
SyntaxElementWithLoc: JSONCSyntaxElement;
|
||||
ConfigNode: JSONCComment;
|
||||
}> {
|
||||
#private;
|
||||
readonly hasBOM: boolean;
|
||||
readonly parserServices: {
|
||||
isJSON?: boolean;
|
||||
parseError?: unknown;
|
||||
};
|
||||
readonly visitorKeys: Record<string, string[]>;
|
||||
private readonly tokenStore;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
constructor(config: {
|
||||
text: string;
|
||||
ast: AST.JSONProgram;
|
||||
hasBOM: boolean;
|
||||
parserServices: {
|
||||
isJSON: boolean;
|
||||
parseError?: unknown;
|
||||
};
|
||||
visitorKeys?: Record<string, string[]> | null | undefined;
|
||||
});
|
||||
traverse(): Iterable<TraversalStep>;
|
||||
/**
|
||||
* Gets all tokens and comments.
|
||||
*/
|
||||
get tokensAndComments(): JSONCTokenOrComment[];
|
||||
getLines(): string[];
|
||||
getAllComments(): JSONCComment[];
|
||||
/**
|
||||
* Returns an array of all inline configuration nodes found in the source code.
|
||||
* This includes eslint-disable, eslint-enable, eslint-disable-line,
|
||||
* eslint-disable-next-line, and eslint (for inline config) comments.
|
||||
*/
|
||||
getInlineConfigNodes(): JSONCComment[];
|
||||
/**
|
||||
* Returns directives that enable or disable rules along with any problems
|
||||
* encountered while parsing the directives.
|
||||
*/
|
||||
getDisableDirectives(): {
|
||||
directives: IDirective[];
|
||||
problems: FileProblem[];
|
||||
};
|
||||
/**
|
||||
* Returns inline rule configurations along with any problems
|
||||
* encountered while parsing the configurations.
|
||||
*/
|
||||
applyInlineConfig(): {
|
||||
configs: {
|
||||
config: {
|
||||
rules: RulesConfig;
|
||||
};
|
||||
loc: AST.SourceLocation;
|
||||
}[];
|
||||
problems: FileProblem[];
|
||||
};
|
||||
/**
|
||||
* Gets the source text for the given node or the entire source if no node is provided.
|
||||
*/
|
||||
getText(node?: JSONCSyntaxElement, beforeCount?: number, afterCount?: number): string;
|
||||
getNodeByRangeIndex(index: number): AST.JSONNode | null;
|
||||
/**
|
||||
* Gets the first token of the given node.
|
||||
*/
|
||||
getFirstToken(node: JSONCSyntaxElement): JSONCToken;
|
||||
/**
|
||||
* Gets the first token of the given node with options.
|
||||
*/
|
||||
getFirstToken(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the first token of the given node with filter options.
|
||||
*/
|
||||
getFirstToken<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the first token of the given node with comment options.
|
||||
*/
|
||||
getFirstToken<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Gets the first tokens of the given node.
|
||||
*/
|
||||
getFirstTokens(node: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets the first tokens of the given node with filter options.
|
||||
*/
|
||||
getFirstTokens<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets the first tokens of the given node with comment options.
|
||||
*/
|
||||
getFirstTokens<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets the last token of the given node.
|
||||
*/
|
||||
getLastToken(node: JSONCSyntaxElement): JSONCToken;
|
||||
/**
|
||||
* Gets the last token of the given node with options.
|
||||
*/
|
||||
getLastToken(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the last token of the given node with filter options.
|
||||
*/
|
||||
getLastToken<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the last token of the given node with comment options.
|
||||
*/
|
||||
getLastToken<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Get the last tokens of the given node.
|
||||
*/
|
||||
getLastTokens(node: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Get the last tokens of the given node with filter options.
|
||||
*/
|
||||
getLastTokens<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Get the last tokens of the given node with comment options.
|
||||
*/
|
||||
getLastTokens<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets the token that precedes a given node or token.
|
||||
*/
|
||||
getTokenBefore(node: JSONCSyntaxElement, options?: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the token that precedes a given node or token with filter options.
|
||||
*/
|
||||
getTokenBefore<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the token that precedes a given node or token with comment options.
|
||||
*/
|
||||
getTokenBefore<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Gets the `count` tokens that precedes a given node or token.
|
||||
*/
|
||||
getTokensBefore(node: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets the `count` tokens that precedes a given node or token with filter options.
|
||||
*/
|
||||
getTokensBefore<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets the `count` tokens that precedes a given node or token with comment options.
|
||||
*/
|
||||
getTokensBefore<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets the token that follows a given node or token.
|
||||
*/
|
||||
getTokenAfter(node: JSONCSyntaxElement, options?: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the token that follows a given node or token with filter options.
|
||||
*/
|
||||
getTokenAfter<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the token that follows a given node or token with comment options.
|
||||
*/
|
||||
getTokenAfter<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Gets the `count` tokens that follows a given node or token.
|
||||
*/
|
||||
getTokensAfter(node: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets the `count` tokens that follows a given node or token with filter options.
|
||||
*/
|
||||
getTokensAfter<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets the `count` tokens that follows a given node or token with comment options.
|
||||
*/
|
||||
getTokensAfter<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets the first token between two non-overlapping nodes.
|
||||
*/
|
||||
getFirstTokenBetween(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options?: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the first token between two non-overlapping nodes with filter options.
|
||||
*/
|
||||
getFirstTokenBetween<R extends JSONCToken>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the first token between two non-overlapping nodes with comment options.
|
||||
*/
|
||||
getFirstTokenBetween<R extends JSONCToken | JSONCComment>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Gets the first tokens between two non-overlapping nodes.
|
||||
*/
|
||||
getFirstTokensBetween(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets the first tokens between two non-overlapping nodes with filter options.
|
||||
*/
|
||||
getFirstTokensBetween<R extends JSONCToken>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets the first tokens between two non-overlapping nodes with comment options.
|
||||
*/
|
||||
getFirstTokensBetween<R extends JSONCToken | JSONCComment>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets the last token between two non-overlapping nodes.
|
||||
*/
|
||||
getLastTokenBetween(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options?: CursorWithSkipOptionsWithoutFilter): JSONCToken | null;
|
||||
/**
|
||||
* Gets the last token between two non-overlapping nodes with filter options.
|
||||
*/
|
||||
getLastTokenBetween<R extends JSONCToken>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithSkipOptionsWithFilter<JSONCToken, R>): R | null;
|
||||
/**
|
||||
* Gets the last token between two non-overlapping nodes with comment options.
|
||||
*/
|
||||
getLastTokenBetween<R extends JSONCToken | JSONCComment>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithSkipOptionsWithComment<JSONCToken, JSONCComment, R>): R | null;
|
||||
/**
|
||||
* Gets the last tokens between two non-overlapping nodes.
|
||||
*/
|
||||
getLastTokensBetween(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets the last tokens between two non-overlapping nodes with filter options.
|
||||
*/
|
||||
getLastTokensBetween<R extends JSONCToken>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets the last tokens between two non-overlapping nodes with comment options.
|
||||
*/
|
||||
getLastTokensBetween<R extends JSONCToken | JSONCComment>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets all tokens that are related to the given node.
|
||||
*/
|
||||
getTokens(node: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets all tokens that are related to the given node with filter options.
|
||||
*/
|
||||
getTokens<R extends JSONCToken>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets all tokens that are related to the given node with comment options.
|
||||
*/
|
||||
getTokens<R extends JSONCToken | JSONCComment>(node: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
/**
|
||||
* Gets all of the tokens between two non-overlapping nodes.
|
||||
*/
|
||||
getTokensBetween(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options?: CursorWithCountOptionsWithoutFilter): JSONCToken[];
|
||||
/**
|
||||
* Gets all of the tokens between two non-overlapping nodes with filter options.
|
||||
*/
|
||||
getTokensBetween<R extends JSONCToken>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithFilter<JSONCToken, R>): R[];
|
||||
/**
|
||||
* Gets all of the tokens between two non-overlapping nodes with comment options.
|
||||
*/
|
||||
getTokensBetween<R extends JSONCToken | JSONCComment>(left: JSONCSyntaxElement, right: JSONCSyntaxElement, options: CursorWithCountOptionsWithComment<JSONCToken, JSONCComment, R>): R[];
|
||||
getCommentsInside(nodeOrToken: JSONCSyntaxElement): JSONCComment[];
|
||||
getCommentsBefore(nodeOrToken: JSONCSyntaxElement): JSONCComment[];
|
||||
getCommentsAfter(nodeOrToken: JSONCSyntaxElement): JSONCComment[];
|
||||
commentsExistBetween(first: JSONCSyntaxElement, second: JSONCSyntaxElement): boolean;
|
||||
isSpaceBetween(first: JSONCToken | JSONCComment, second: JSONCToken | JSONCComment): boolean;
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated JSONC does not have scopes
|
||||
*/
|
||||
getScope(node?: AST.JSONNode): Scope.Scope | null;
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated JSONC does not have scopes
|
||||
*/
|
||||
get scopeManager(): Scope.ScopeManager | null;
|
||||
/**
|
||||
* Compatibility for ESLint's SourceCode API
|
||||
* @deprecated
|
||||
*/
|
||||
isSpaceBetweenTokens(first: JSONCTokenOrComment, second: JSONCTokenOrComment): boolean;
|
||||
private _getChildren;
|
||||
}
|
||||
//#endregion
|
||||
//#region lib/language/jsonc-language.d.ts
|
||||
/**
|
||||
* Language options for JSONC.
|
||||
*/
|
||||
type JSONCLanguageOptions = {
|
||||
parserOptions?: JSONParserOptions;
|
||||
};
|
||||
type ParserMode = "JSON" | "JSONC" | "JSON5" | "EXTENDED";
|
||||
type JSONCLanguageInstanceOptions = {
|
||||
mode?: ParserMode;
|
||||
};
|
||||
/**
|
||||
* The JSONC language implementation for ESLint.
|
||||
*/
|
||||
declare class JSONCLanguage implements Language<{
|
||||
LangOptions: JSONCLanguageOptions;
|
||||
Code: JSONCSourceCode;
|
||||
RootNode: AST.JSONProgram;
|
||||
Node: AST.JSONNode;
|
||||
}> {
|
||||
/**
|
||||
* The type of file to read.
|
||||
*/
|
||||
fileType: "text";
|
||||
/**
|
||||
* The line number at which the parser starts counting.
|
||||
*/
|
||||
lineStart: 1;
|
||||
/**
|
||||
* The column number at which the parser starts counting.
|
||||
*/
|
||||
columnStart: 0;
|
||||
/**
|
||||
* The name of the key that holds the type of the node.
|
||||
*/
|
||||
nodeTypeKey: "type";
|
||||
private readonly _mode;
|
||||
constructor(options?: JSONCLanguageInstanceOptions);
|
||||
/**
|
||||
* Validates the language options.
|
||||
*/
|
||||
validateLanguageOptions(_languageOptions: JSONCLanguageOptions): void;
|
||||
normalizeLanguageOptions(languageOptions: JSONCLanguageOptions): JSONCLanguageOptions;
|
||||
/**
|
||||
* Parses the given file into an AST.
|
||||
*/
|
||||
parse(file: File, context: {
|
||||
languageOptions?: JSONCLanguageOptions;
|
||||
}): OkParseResult<AST.JSONProgram> | NotOkParseResult;
|
||||
/**
|
||||
* Creates a new SourceCode object for the given file and parse result.
|
||||
*/
|
||||
createSourceCode(file: File, parseResult: OkParseResult<AST.JSONProgram>): JSONCSourceCode;
|
||||
}
|
||||
//#endregion
|
||||
//#region lib/types.d.ts
|
||||
interface RuleModule<RuleOptions extends unknown[] = unknown[]> extends core.RuleDefinition<{
|
||||
LangOptions: JSONCLanguageOptions;
|
||||
Code: JSONCSourceCode;
|
||||
RuleOptions: RuleOptions;
|
||||
Visitor: RuleListener;
|
||||
Node: AST.JSONNode;
|
||||
MessageIds: string;
|
||||
ExtRuleDocs: RuleMetaDocs;
|
||||
}> {
|
||||
meta: RuleMetaData<RuleOptions>;
|
||||
}
|
||||
type RuleMetaDocs = {
|
||||
description: string;
|
||||
recommended: ("json" | "jsonc" | "json5")[] | null;
|
||||
url: string;
|
||||
ruleId: string;
|
||||
ruleName: string;
|
||||
default?: "error" | "warn";
|
||||
extensionRule: boolean | string | {
|
||||
plugin: string;
|
||||
url: string;
|
||||
};
|
||||
layout: boolean;
|
||||
};
|
||||
interface RuleMetaData<RuleOptions extends unknown[] = unknown[]> extends core.RulesMeta<string, RuleOptions, RuleMetaDocs> {
|
||||
docs: RuleMetaDocs;
|
||||
}
|
||||
//#endregion
|
||||
export { JSONCSourceCode as a, JSONCComment as i, JSONCLanguage as n, JSONCToken as o, JSONCLanguageOptions as r, RuleModule as t };
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# ESLint Core
|
||||
|
||||
## Overview
|
||||
|
||||
This package is the future home of the rewritten, runtime-agnostic ESLint core.
|
||||
|
||||
Right now, it exports the core types necessary to implement language plugins.
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0
|
||||
|
||||
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
||||
<!--sponsorsstart-->
|
||||
|
||||
## Sponsors
|
||||
|
||||
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
|
||||
to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
||||
|
||||
<h3>Platinum Sponsors</h3>
|
||||
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
|
||||
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
|
||||
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
|
||||
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="#"><img src="https://images.opencollective.com/aeriusventilations-org/avatar.png" alt="aeriusventilation's Org" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
|
||||
<h3>Technology Sponsors</h3>
|
||||
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
|
||||
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
|
||||
<!--sponsorsend-->
|
||||
Generated
Vendored
+1159
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+1159
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "@eslint/core",
|
||||
"version": "1.2.1",
|
||||
"description": "Runtime-agnostic core of ESLint",
|
||||
"type": "module",
|
||||
"types": "./dist/esm/types.d.ts",
|
||||
"exports": {
|
||||
"types": {
|
||||
"import": "./dist/esm/types.d.ts",
|
||||
"require": "./dist/cjs/types.d.cts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"build:cts": "node -e \"fs.cpSync('dist/esm/types.d.ts', 'dist/cjs/types.d.cts')\"",
|
||||
"build": "tsc && npm run build:cts",
|
||||
"lint:types": "attw --pack",
|
||||
"pretest": "npm run build",
|
||||
"test": "npm run test:types",
|
||||
"test:jsr": "npx -y jsr@latest publish --dry-run",
|
||||
"test:types": "tsc -p tests/types/tsconfig.json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/eslint/rewrite.git",
|
||||
"directory": "packages/core"
|
||||
},
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"core"
|
||||
],
|
||||
"author": "Nicholas C. Zakas",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/eslint/rewrite/issues"
|
||||
},
|
||||
"homepage": "https://github.com/eslint/rewrite/tree/main/packages/core#readme",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"json-schema": "^0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Generated
Vendored
+273
@@ -0,0 +1,273 @@
|
||||
# ESLint Plugin Kit
|
||||
|
||||
## Description
|
||||
|
||||
A collection of utilities to help build ESLint plugins.
|
||||
|
||||
## Installation
|
||||
|
||||
For Node.js and compatible runtimes:
|
||||
|
||||
```shell
|
||||
npm install @eslint/plugin-kit
|
||||
# or
|
||||
yarn add @eslint/plugin-kit
|
||||
# or
|
||||
pnpm install @eslint/plugin-kit
|
||||
# or
|
||||
bun add @eslint/plugin-kit
|
||||
```
|
||||
|
||||
For Deno:
|
||||
|
||||
```shell
|
||||
deno add @eslint/plugin-kit
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
This package exports the following utilities:
|
||||
|
||||
- [`ConfigCommentParser`](#configcommentparser) - used to parse ESLint configuration comments (i.e., `/* eslint-disable rule */`)
|
||||
- [`VisitNodeStep` and `CallMethodStep`](#visitnodestep-and-callmethodstep) - used to help implement `SourceCode#traverse()`
|
||||
- [`Directive`](#directive) - used to help implement `SourceCode#getDisableDirectives()`
|
||||
- [`TextSourceCodeBase`](#textsourcecodebase) - base class to help implement the `SourceCode` interface
|
||||
|
||||
### `ConfigCommentParser`
|
||||
|
||||
To use the `ConfigCommentParser` class, import it from the package and create a new instance, such as:
|
||||
|
||||
```js
|
||||
import { ConfigCommentParser } from "@eslint/plugin-kit";
|
||||
|
||||
// create a new instance
|
||||
const commentParser = new ConfigCommentParser();
|
||||
|
||||
// pass in a comment string without the comment delimiters
|
||||
const directive = commentParser.parseDirective(
|
||||
"eslint-disable prefer-const, no-var -- I don't want to use these.",
|
||||
);
|
||||
|
||||
// will be undefined when a directive can't be parsed
|
||||
if (directive) {
|
||||
console.log(directive.label); // "eslint-disable"
|
||||
console.log(directive.value); // "prefer-const, no-var"
|
||||
console.log(directive.justification); // "I don't want to use these."
|
||||
}
|
||||
```
|
||||
|
||||
There are different styles of directive values that you'll need to parse separately to get the correct format:
|
||||
|
||||
```js
|
||||
import { ConfigCommentParser } from "@eslint/plugin-kit";
|
||||
|
||||
// create a new instance
|
||||
const commentParser = new ConfigCommentParser();
|
||||
|
||||
// list format
|
||||
const list = commentParser.parseListConfig("prefer-const, no-var");
|
||||
console.log(Object.entries(list)); // [["prefer-const", true], ["no-var", true]]
|
||||
|
||||
// string format
|
||||
const strings = commentParser.parseStringConfig("foo:off, bar");
|
||||
console.log(Object.entries(strings)); // [["foo", "off"], ["bar", null]]
|
||||
|
||||
// JSON-like config format
|
||||
const jsonLike = commentParser.parseJSONLikeConfig(
|
||||
"radix:[error, always], prefer-const: warn",
|
||||
);
|
||||
console.log(Object.entries(jsonLike.config)); // [["radix", ["error", "always"]], ["prefer-const", "warn"]]
|
||||
```
|
||||
|
||||
### `VisitNodeStep` and `CallMethodStep`
|
||||
|
||||
The `VisitNodeStep` and `CallMethodStep` classes represent steps in the traversal of source code. They implement the correct interfaces to return from the `SourceCode#traverse()` method.
|
||||
|
||||
The `VisitNodeStep` class is the more common of the two, where you are describing a visit to a particular node during the traversal. The constructor accepts three arguments:
|
||||
|
||||
- `target` - the node being visited. This is used to determine the method to call inside of a rule. For instance, if the node's type is `Literal` then ESLint will call a method named `Literal()` on the rule (if present).
|
||||
- `phase` - either 1 for enter or 2 for exit.
|
||||
- `args` - an array of arguments to pass into the visitor method of a rule.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
import { VisitNodeStep } from "@eslint/plugin-kit";
|
||||
|
||||
class MySourceCode {
|
||||
traverse() {
|
||||
const steps = [];
|
||||
|
||||
for (const { node, parent, phase } of iterator(this.ast)) {
|
||||
steps.push(
|
||||
new VisitNodeStep({
|
||||
target: node,
|
||||
phase: phase === "enter" ? 1 : 2,
|
||||
args: [node, parent],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `CallMethodStep` class is less common and is used to tell ESLint to call a specific method on the rule. The constructor accepts two arguments:
|
||||
|
||||
- `target` - the name of the method to call, frequently beginning with `"on"` such as `"onCodePathStart"`.
|
||||
- `args` - an array of arguments to pass to the method.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
import { VisitNodeStep, CallMethodStep } from "@eslint/plugin-kit";
|
||||
|
||||
class MySourceCode {
|
||||
traverse() {
|
||||
const steps = [];
|
||||
|
||||
for (const { node, parent, phase } of iterator(this.ast)) {
|
||||
steps.push(
|
||||
new VisitNodeStep({
|
||||
target: node,
|
||||
phase: phase === "enter" ? 1 : 2,
|
||||
args: [node, parent],
|
||||
}),
|
||||
);
|
||||
|
||||
// call a method indicating how many times we've been through the loop
|
||||
steps.push(
|
||||
new CallMethodStep({
|
||||
target: "onIteration",
|
||||
args: [steps.length]
|
||||
});
|
||||
)
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `Directive`
|
||||
|
||||
The `Directive` class represents a disable directive in the source code and implements the `Directive` interface from `@eslint/core`. You can tell ESLint about disable directives using the `SourceCode#getDisableDirectives()` method, where part of the return value is an array of `Directive` objects. Here's an example:
|
||||
|
||||
```js
|
||||
import { Directive, ConfigCommentParser } from "@eslint/plugin-kit";
|
||||
|
||||
class MySourceCode {
|
||||
getDisableDirectives() {
|
||||
const directives = [];
|
||||
const problems = [];
|
||||
const commentParser = new ConfigCommentParser();
|
||||
|
||||
// read in the inline config nodes to check each one
|
||||
this.getInlineConfigNodes().forEach(comment => {
|
||||
// Step 1: Parse the directive
|
||||
const { label, value, justification } =
|
||||
commentParser.parseDirective(comment.value);
|
||||
|
||||
// Step 2: Extract the directive value and create the `Directive` object
|
||||
switch (label) {
|
||||
case "eslint-disable":
|
||||
case "eslint-enable":
|
||||
case "eslint-disable-next-line":
|
||||
case "eslint-disable-line": {
|
||||
const directiveType = label.slice("eslint-".length);
|
||||
|
||||
directives.push(
|
||||
new Directive({
|
||||
type: directiveType,
|
||||
node: comment,
|
||||
value,
|
||||
justification,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// ignore any comments that don't begin with known labels
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
directives,
|
||||
problems,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `TextSourceCodeBase`
|
||||
|
||||
The `TextSourceCodeBase` class is intended to be a base class that has several of the common members found in `SourceCode` objects already implemented. Those members are:
|
||||
|
||||
- `lines` - an array of text lines that is created automatically when the constructor is called.
|
||||
- `getLoc(nodeOrToken)` - gets the location of a node or token. Works for nodes that have the ESLint-style `loc` property and nodes that have the Unist-style [`position` property](https://github.com/syntax-tree/unist?tab=readme-ov-file#position). If you're using an AST with a different location format, you'll still need to implement this method yourself.
|
||||
- `getLocFromIndex(index)` - Converts a source text index into a `{ line: number, column: number }` pair. (For this method to work, the root node should always cover the entire source code text, and the `getLoc()` method needs to be implemented correctly.)
|
||||
- `getIndexFromLoc(loc)` - Converts a `{ line: number, column: number }` pair into a source text index. (For this method to work, the root node should always cover the entire source code text, and the `getLoc()` method needs to be implemented correctly.)
|
||||
- `getRange(nodeOrToken)` - gets the range of a node or token within the source text. Works for nodes that have the ESLint-style `range` property and nodes that have the Unist-style [`position` property](https://github.com/syntax-tree/unist?tab=readme-ov-file#position). If you're using an AST with a different range format, you'll still need to implement this method yourself.
|
||||
- `getText(node, beforeCount, afterCount)` - gets the source text for the given node that has range information attached. Optionally, can return additional characters before and after the given node. As long as `getRange()` is properly implemented, this method will just work.
|
||||
- `getAncestors(node)` - returns the ancestry of the node. In order for this to work, you must implement the `getParent()` method yourself.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```js
|
||||
import { TextSourceCodeBase } from "@eslint/plugin-kit";
|
||||
|
||||
export class MySourceCode extends TextSourceCodeBase {
|
||||
#parents = new Map();
|
||||
|
||||
constructor({ ast, text }) {
|
||||
super({ ast, text });
|
||||
}
|
||||
|
||||
getParent(node) {
|
||||
return this.#parents.get(node);
|
||||
}
|
||||
|
||||
traverse() {
|
||||
const steps = [];
|
||||
|
||||
for (const { node, parent, phase } of iterator(this.ast)) {
|
||||
//save the parent information
|
||||
this.#parent.set(node, parent);
|
||||
|
||||
steps.push(
|
||||
new VisitNodeStep({
|
||||
target: node,
|
||||
phase: phase === "enter" ? 1 : 2,
|
||||
args: [node, parent],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In general, it's safe to collect the parent information during the `traverse()` method as `getParent()` and `getAncestor()` will only be called from rules once the AST has been traversed at least once.
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0
|
||||
|
||||
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
||||
<!--sponsorsstart-->
|
||||
|
||||
## Sponsors
|
||||
|
||||
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
|
||||
to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
||||
|
||||
<h3>Platinum Sponsors</h3>
|
||||
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
|
||||
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
|
||||
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
|
||||
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
|
||||
<h3>Technology Sponsors</h3>
|
||||
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
|
||||
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
|
||||
<!--sponsorsend-->
|
||||
Generated
Vendored
+895
@@ -0,0 +1,895 @@
|
||||
'use strict';
|
||||
|
||||
var levn = require('levn');
|
||||
|
||||
/**
|
||||
* @fileoverview Config Comment Parser
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @import * as $eslintcore from "@eslint/core"; */
|
||||
/** @typedef {$eslintcore.RuleConfig} RuleConfig */
|
||||
/** @typedef {$eslintcore.RulesConfig} RulesConfig */
|
||||
/** @import * as $typests from "./types.ts"; */
|
||||
/** @typedef {$typests.StringConfig} StringConfig */
|
||||
/** @typedef {$typests.BooleanConfig} BooleanConfig */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const directivesPattern = /^([a-z]+(?:-[a-z]+)*)(?:\s|$)/u;
|
||||
const validSeverities = new Set([0, 1, 2, "off", "warn", "error"]);
|
||||
|
||||
/**
|
||||
* Determines if the severity in the rule configuration is valid.
|
||||
* @param {RuleConfig} ruleConfig A rule's configuration.
|
||||
* @returns {boolean} `true` if the severity is valid, otherwise `false`.
|
||||
*/
|
||||
function isSeverityValid(ruleConfig) {
|
||||
const severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
|
||||
return validSeverities.has(severity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if all severities in the rules configuration are valid.
|
||||
* @param {RulesConfig} rulesConfig The rules configuration to check.
|
||||
* @returns {boolean} `true` if all severities are valid, otherwise `false`.
|
||||
*/
|
||||
function isEverySeverityValid(rulesConfig) {
|
||||
return Object.values(rulesConfig).every(isSeverityValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a directive comment.
|
||||
*/
|
||||
class DirectiveComment {
|
||||
/**
|
||||
* The label of the directive, such as "eslint", "eslint-disable", etc.
|
||||
* @type {string}
|
||||
*/
|
||||
label = "";
|
||||
|
||||
/**
|
||||
* The value of the directive (the string after the label).
|
||||
* @type {string}
|
||||
*/
|
||||
value = "";
|
||||
|
||||
/**
|
||||
* The justification of the directive (the string after the --).
|
||||
* @type {string}
|
||||
*/
|
||||
justification = "";
|
||||
|
||||
/**
|
||||
* Creates a new directive comment.
|
||||
* @param {string} label The label of the directive.
|
||||
* @param {string} value The value of the directive.
|
||||
* @param {string} justification The justification of the directive.
|
||||
*/
|
||||
constructor(label, value, justification) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
this.justification = justification;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Object to parse ESLint configuration comments.
|
||||
*/
|
||||
class ConfigCommentParser {
|
||||
/**
|
||||
* Parses a list of "name:string_value" or/and "name" options divided by comma or
|
||||
* whitespace. Used for "global" comments.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {StringConfig} Result map object of names and string values, or null values if no value was provided.
|
||||
*/
|
||||
parseStringConfig(string) {
|
||||
const items = /** @type {StringConfig} */ ({});
|
||||
|
||||
// Collapse whitespace around `:` and `,` to make parsing easier
|
||||
const trimmedString = string
|
||||
.trim()
|
||||
.replace(/(?<!\s)\s*([:,])\s*/gu, "$1");
|
||||
|
||||
trimmedString.split(/\s|,+/u).forEach(name => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// value defaults to null (if not provided), e.g: "foo" => ["foo", null]
|
||||
const [key, value = null] = name.split(":");
|
||||
|
||||
items[key] = value;
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON-like config.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {({ok: true, config: RulesConfig}|{ok: false, error: {message: string}})} Result map object
|
||||
*/
|
||||
parseJSONLikeConfig(string) {
|
||||
// Parses a JSON-like comment by the same way as parsing CLI option.
|
||||
try {
|
||||
const items =
|
||||
/** @type {RulesConfig} */ (levn.parse("Object", string)) || {};
|
||||
|
||||
/*
|
||||
* When the configuration has any invalid severities, it should be completely
|
||||
* ignored. This is because the configuration is not valid and should not be
|
||||
* applied.
|
||||
*
|
||||
* For example, the following configuration is invalid:
|
||||
*
|
||||
* "no-alert: 2 no-console: 2"
|
||||
*
|
||||
* This results in a configuration of { "no-alert": "2 no-console: 2" }, which is
|
||||
* not valid. In this case, the configuration should be ignored.
|
||||
*/
|
||||
if (isEverySeverityValid(items)) {
|
||||
return {
|
||||
ok: true,
|
||||
config: items,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// levn parsing error: ignore to parse the string by a fallback.
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionator cannot parse commaless notations.
|
||||
* But we are supporting that. So this is a fallback for that.
|
||||
*/
|
||||
const normalizedString = string
|
||||
.replace(/(?<![-a-zA-Z0-9/])([-a-zA-Z0-9/]+):/gu, '"$1":')
|
||||
.replace(/([\]0-9])\s+(?=")/u, "$1,");
|
||||
|
||||
try {
|
||||
const items = JSON.parse(`{${normalizedString}}`);
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
config: items,
|
||||
};
|
||||
} catch (ex) {
|
||||
const errorMessage = ex instanceof Error ? ex.message : String(ex);
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
error: {
|
||||
message: `Failed to parse JSON from '${normalizedString}': ${errorMessage}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a config of values separated by comma.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {BooleanConfig} Result map of values and true values
|
||||
*/
|
||||
parseListConfig(string) {
|
||||
const items = /** @type {BooleanConfig} */ ({});
|
||||
|
||||
string.split(",").forEach(name => {
|
||||
const trimmedName = name
|
||||
.trim()
|
||||
.replace(
|
||||
/^(?<quote>['"]?)(?<ruleId>.*)\k<quote>$/su,
|
||||
"$<ruleId>",
|
||||
);
|
||||
|
||||
if (trimmedName) {
|
||||
items[trimmedName] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the directive and the justification from a given directive comment and trim them.
|
||||
* @param {string} value The comment text to extract.
|
||||
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
|
||||
*/
|
||||
#extractDirectiveComment(value) {
|
||||
const match = /\s-{2,}\s/u.exec(value);
|
||||
|
||||
if (!match) {
|
||||
return { directivePart: value.trim(), justificationPart: "" };
|
||||
}
|
||||
|
||||
const directive = value.slice(0, match.index).trim();
|
||||
const justification = value.slice(match.index + match[0].length).trim();
|
||||
|
||||
return { directivePart: directive, justificationPart: justification };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a directive comment into directive text and value.
|
||||
* @param {string} string The string with the directive to be parsed.
|
||||
* @returns {DirectiveComment|undefined} The parsed directive or `undefined` if the directive is invalid.
|
||||
*/
|
||||
parseDirective(string) {
|
||||
const { directivePart, justificationPart } =
|
||||
this.#extractDirectiveComment(string);
|
||||
const match = directivesPattern.exec(directivePart);
|
||||
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const directiveText = match[1];
|
||||
const directiveValue = directivePart.slice(
|
||||
match.index + directiveText.length,
|
||||
);
|
||||
|
||||
return new DirectiveComment(
|
||||
directiveText,
|
||||
directiveValue.trim(),
|
||||
justificationPart,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @fileoverview A collection of helper classes for implementing `SourceCode`.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
/* eslint class-methods-use-this: off -- Required to complete interface. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {$eslintcore.VisitTraversalStep} VisitTraversalStep */
|
||||
/** @typedef {$eslintcore.CallTraversalStep} CallTraversalStep */
|
||||
/** @typedef {$eslintcore.TraversalStep} TraversalStep */
|
||||
/** @typedef {$eslintcore.SourceLocation} SourceLocation */
|
||||
/** @typedef {$eslintcore.SourceLocationWithOffset} SourceLocationWithOffset */
|
||||
/** @typedef {$eslintcore.SourceRange} SourceRange */
|
||||
/** @typedef {$eslintcore.Directive} IDirective */
|
||||
/** @typedef {$eslintcore.DirectiveType} DirectiveType */
|
||||
/** @typedef {$eslintcore.SourceCodeBaseTypeOptions} SourceCodeBaseTypeOptions */
|
||||
/**
|
||||
* @typedef {import("@eslint/core").TextSourceCode<Options>} TextSourceCode
|
||||
* @template {SourceCodeBaseTypeOptions} [Options=SourceCodeBaseTypeOptions]
|
||||
*/
|
||||
/** @typedef {$eslintcore.RuleVisitor} RuleVisitor */
|
||||
/**
|
||||
* @typedef {import("./types.ts").CustomRuleVisitorWithExit<RuleVisitorType>} CustomRuleVisitorWithExit
|
||||
* @template {RuleVisitor} RuleVisitorType
|
||||
*/
|
||||
/** @typedef {$typests.CustomRuleTypeDefinitions} CustomRuleTypeDefinitions */
|
||||
/**
|
||||
* @typedef {import("./types.ts").CustomRuleDefinitionType<LanguageSpecificOptions, Options>} CustomRuleDefinitionType
|
||||
* @template {Omit<import("@eslint/core").RuleDefinitionTypeOptions, keyof CustomRuleTypeDefinitions>} LanguageSpecificOptions
|
||||
* @template {Partial<CustomRuleTypeDefinitions>} Options
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if a node has ESTree-style loc information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {loc:SourceLocation}} `true` if the node has ESTree-style loc information, `false` if not.
|
||||
*/
|
||||
function hasESTreeStyleLoc(node) {
|
||||
return "loc" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has position-style loc information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {position:SourceLocation}} `true` if the node has position-style range information, `false` if not.
|
||||
*/
|
||||
function hasPosStyleLoc(node) {
|
||||
return "position" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has ESTree-style range information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {range:SourceRange}} `true` if the node has ESTree-style range information, `false` if not.
|
||||
*/
|
||||
function hasESTreeStyleRange(node) {
|
||||
return "range" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has position-style range information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {position:SourceLocationWithOffset}} `true` if the node has position-style range information, `false` if not.
|
||||
*/
|
||||
function hasPosStyleRange(node) {
|
||||
return "position" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs binary search to find the line number containing a given target index.
|
||||
* Returns the lower bound - the index of the first element greater than the target.
|
||||
* **Please note that the `lineStartIndices` should be sorted in ascending order**.
|
||||
* - Time Complexity: O(log n) - Significantly faster than linear search for large files.
|
||||
* @param {number[]} lineStartIndices Sorted array of line start indices.
|
||||
* @param {number} targetIndex The target index to find the line number for.
|
||||
* @returns {number} The line number for the target index.
|
||||
*/
|
||||
function findLineNumberBinarySearch(lineStartIndices, targetIndex) {
|
||||
let low = 0;
|
||||
let high = lineStartIndices.length - 1;
|
||||
|
||||
while (low < high) {
|
||||
const mid = ((low + high) / 2) | 0; // Use bitwise OR to floor the division.
|
||||
|
||||
if (targetIndex < lineStartIndices[mid]) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a node is visited.
|
||||
* @implements {VisitTraversalStep}
|
||||
*/
|
||||
class VisitNodeStep {
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"visit"}
|
||||
* @readonly
|
||||
*/
|
||||
type = "visit";
|
||||
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {1}
|
||||
* @readonly
|
||||
*/
|
||||
kind = 1;
|
||||
|
||||
/**
|
||||
* The target of the step.
|
||||
* @type {object}
|
||||
*/
|
||||
target;
|
||||
|
||||
/**
|
||||
* The phase of the step.
|
||||
* @type {1|2}
|
||||
*/
|
||||
phase;
|
||||
|
||||
/**
|
||||
* The arguments of the step.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {object} options.target The target of the step.
|
||||
* @param {1|2} options.phase The phase of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, phase, args }) {
|
||||
this.target = target;
|
||||
this.phase = phase;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a
|
||||
* method is called.
|
||||
* @implements {CallTraversalStep}
|
||||
*/
|
||||
class CallMethodStep {
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"call"}
|
||||
* @readonly
|
||||
*/
|
||||
type = "call";
|
||||
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {2}
|
||||
* @readonly
|
||||
*/
|
||||
kind = 2;
|
||||
|
||||
/**
|
||||
* The name of the method to call.
|
||||
* @type {string}
|
||||
*/
|
||||
target;
|
||||
|
||||
/**
|
||||
* The arguments to pass to the method.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {string} options.target The target of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, args }) {
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to represent a directive comment.
|
||||
* @implements {IDirective}
|
||||
*/
|
||||
class Directive {
|
||||
/**
|
||||
* The type of directive.
|
||||
* @type {DirectiveType}
|
||||
* @readonly
|
||||
*/
|
||||
type;
|
||||
|
||||
/**
|
||||
* The node representing the directive.
|
||||
* @type {unknown}
|
||||
* @readonly
|
||||
*/
|
||||
node;
|
||||
|
||||
/**
|
||||
* Everything after the "eslint-disable" portion of the directive,
|
||||
* but before the "--" that indicates the justification.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
value;
|
||||
|
||||
/**
|
||||
* The justification for the directive.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
justification;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the directive.
|
||||
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
|
||||
* @param {unknown} options.node The node representing the directive.
|
||||
* @param {string} options.value The value of the directive.
|
||||
* @param {string} options.justification The justification for the directive.
|
||||
*/
|
||||
constructor({ type, node, value, justification }) {
|
||||
this.type = type;
|
||||
this.node = node;
|
||||
this.value = value;
|
||||
this.justification = justification;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source Code Base Object
|
||||
* @template {SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}} [Options=SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}]
|
||||
* @implements {TextSourceCode<Options>}
|
||||
*/
|
||||
class TextSourceCodeBase {
|
||||
/**
|
||||
* The lines of text in the source code.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
#lines = [];
|
||||
|
||||
/**
|
||||
* The indices of the start of each line in the source code.
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
#lineStartIndices = [0];
|
||||
|
||||
/**
|
||||
* The pattern to match lineEndings in the source code.
|
||||
* @type {RegExp}
|
||||
*/
|
||||
#lineEndingPattern;
|
||||
|
||||
/**
|
||||
* The AST of the source code.
|
||||
* @type {Options['RootNode']}
|
||||
*/
|
||||
ast;
|
||||
|
||||
/**
|
||||
* The text of the source code.
|
||||
* @type {string}
|
||||
*/
|
||||
text;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the instance.
|
||||
* @param {string} options.text The source code text.
|
||||
* @param {Options['RootNode']} options.ast The root AST node.
|
||||
* @param {RegExp} [options.lineEndingPattern] The pattern to match lineEndings in the source code. Defaults to `/\r?\n/u`.
|
||||
*/
|
||||
constructor({ text, ast, lineEndingPattern = /\r?\n/u }) {
|
||||
this.ast = ast;
|
||||
this.text = text;
|
||||
// Remove the global(`g`) and sticky(`y`) flags from the `lineEndingPattern` to avoid issues with lastIndex.
|
||||
this.#lineEndingPattern = new RegExp(
|
||||
lineEndingPattern.source,
|
||||
lineEndingPattern.flags.replace(/[gy]/gu, ""),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the next line in the source text and updates `#lines` and `#lineStartIndices`.
|
||||
* @param {string} text The text to search for the next line.
|
||||
* @returns {boolean} `true` if a next line was found, `false` otherwise.
|
||||
*/
|
||||
#findNextLine(text) {
|
||||
const match = this.#lineEndingPattern.exec(text);
|
||||
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.#lines.push(text.slice(0, match.index));
|
||||
this.#lineStartIndices.push(
|
||||
(this.#lineStartIndices.at(-1) ?? 0) +
|
||||
match.index +
|
||||
match[0].length,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lines` is lazily calculated from the source text.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLines() {
|
||||
// If `#lines` has already been calculated, do nothing.
|
||||
if (this.#lines.length === this.#lineStartIndices.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found.
|
||||
}
|
||||
|
||||
this.#lines.push(this.text.slice(this.#lineStartIndices.at(-1)));
|
||||
|
||||
Object.freeze(this.#lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lineStartIndices` is lazily calculated up to the specified index.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLineStartIndicesFromIndex(index) {
|
||||
// If we've already parsed up to or beyond this index, do nothing.
|
||||
if (index <= (this.#lineStartIndices.at(-1) ?? 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
index > (this.#lineStartIndices.at(-1) ?? 0) &&
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lineStartIndices` is lazily calculated up to the specified loc.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} lineStart The line number at which the parser starts counting.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLineStartIndicesFromLoc(loc, lineStart) {
|
||||
// Calculate line indices up to the potentially next line, as it is needed for the follow‑up calculation.
|
||||
const nextLocLineIndex = loc.line - lineStart + 1;
|
||||
const lastCalculatedLineIndex = this.#lineStartIndices.length - 1;
|
||||
let additionalLinesNeeded = nextLocLineIndex - lastCalculatedLineIndex;
|
||||
|
||||
// If we've already parsed up to or beyond this line, do nothing.
|
||||
if (additionalLinesNeeded <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
additionalLinesNeeded > 0 &&
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found or we have enough lines.
|
||||
additionalLinesNeeded -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loc information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the loc information for.
|
||||
* @returns {SourceLocation} The loc information for the node or token.
|
||||
* @throws {Error} If the node or token does not have loc information.
|
||||
*/
|
||||
getLoc(nodeOrToken) {
|
||||
if (hasESTreeStyleLoc(nodeOrToken)) {
|
||||
return nodeOrToken.loc;
|
||||
}
|
||||
|
||||
if (hasPosStyleLoc(nodeOrToken)) {
|
||||
return nodeOrToken.position;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Custom getLoc() method must be implemented in the subclass.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a source text index into a `{ line: number, column: number }` pair.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @throws {TypeError|RangeError} If non-numeric index or index out of range.
|
||||
* @returns {{line: number, column: number}} A `{ line: number, column: number }` location object with 0 or 1-indexed line and 0 or 1-indexed column based on language.
|
||||
* @public
|
||||
*/
|
||||
getLocFromIndex(index) {
|
||||
if (typeof index !== "number") {
|
||||
throw new TypeError("Expected `index` to be a number.");
|
||||
}
|
||||
|
||||
if (index < 0 || index > this.text.length) {
|
||||
throw new RangeError(
|
||||
`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`,
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
start: { line: lineStart, column: columnStart },
|
||||
end: { line: lineEnd, column: columnEnd },
|
||||
} = this.getLoc(this.ast);
|
||||
|
||||
// If the index is at the start, return the start location of the root node.
|
||||
if (index === 0) {
|
||||
return {
|
||||
line: lineStart,
|
||||
column: columnStart,
|
||||
};
|
||||
}
|
||||
|
||||
// If the index is `this.text.length`, return the location one "spot" past the last character of the file.
|
||||
if (index === this.text.length) {
|
||||
return {
|
||||
line: lineEnd,
|
||||
column: columnEnd,
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure `#lineStartIndices` are lazily calculated.
|
||||
this.#ensureLineStartIndicesFromIndex(index);
|
||||
|
||||
/*
|
||||
* To figure out which line `index` is on, determine the last place at which index could
|
||||
* be inserted into `#lineStartIndices` to keep the list sorted.
|
||||
*/
|
||||
const lineNumber =
|
||||
(index >= (this.#lineStartIndices.at(-1) ?? 0)
|
||||
? this.#lineStartIndices.length
|
||||
: findLineNumberBinarySearch(this.#lineStartIndices, index)) -
|
||||
1 +
|
||||
lineStart;
|
||||
|
||||
return {
|
||||
line: lineNumber,
|
||||
column:
|
||||
index -
|
||||
this.#lineStartIndices[lineNumber - lineStart] +
|
||||
columnStart,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a `{ line: number, column: number }` pair into a source text index.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} loc.column The column number of the location. (0 or 1-indexed based on language.)
|
||||
* @throws {TypeError|RangeError} If `loc` is not an object with a numeric
|
||||
* `line` and `column`, if the `line` is less than or equal to zero or
|
||||
* the `line` or `column` is out of the expected range.
|
||||
* @returns {number} The index of the line/column location in a file.
|
||||
* @public
|
||||
*/
|
||||
getIndexFromLoc(loc) {
|
||||
if (
|
||||
loc === null ||
|
||||
typeof loc !== "object" ||
|
||||
typeof loc.line !== "number" ||
|
||||
typeof loc.column !== "number"
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Expected `loc` to be an object with numeric `line` and `column` properties.",
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
start: { line: lineStart, column: columnStart },
|
||||
end: { line: lineEnd, column: columnEnd },
|
||||
} = this.getLoc(this.ast);
|
||||
|
||||
if (loc.line < lineStart || lineEnd < loc.line) {
|
||||
throw new RangeError(
|
||||
`Line number out of range (line ${loc.line} requested). Valid range: ${lineStart}-${lineEnd}`,
|
||||
);
|
||||
}
|
||||
|
||||
// If the loc is at the start, return the start index of the root node.
|
||||
if (loc.line === lineStart && loc.column === columnStart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the loc is at the end, return the index one "spot" past the last character of the file.
|
||||
if (loc.line === lineEnd && loc.column === columnEnd) {
|
||||
return this.text.length;
|
||||
}
|
||||
|
||||
// Ensure `#lineStartIndices` are lazily calculated.
|
||||
this.#ensureLineStartIndicesFromLoc(loc, lineStart);
|
||||
|
||||
const isLastLine = loc.line === lineEnd;
|
||||
const lineStartIndex = this.#lineStartIndices[loc.line - lineStart];
|
||||
const lineEndIndex = isLastLine
|
||||
? this.text.length
|
||||
: this.#lineStartIndices[loc.line - lineStart + 1];
|
||||
const positionIndex = lineStartIndex + loc.column - columnStart;
|
||||
|
||||
if (
|
||||
loc.column < columnStart ||
|
||||
(isLastLine && positionIndex > lineEndIndex) ||
|
||||
(!isLastLine && positionIndex >= lineEndIndex)
|
||||
) {
|
||||
throw new RangeError(
|
||||
`Column number out of range (column ${loc.column} requested). Valid range for line ${loc.line}: ${columnStart}-${lineEndIndex - lineStartIndex + columnStart + (isLastLine ? 0 : -1)}`,
|
||||
);
|
||||
}
|
||||
|
||||
return positionIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the range information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the range information for.
|
||||
* @returns {SourceRange} The range information for the node or token.
|
||||
* @throws {Error} If the node or token does not have range information.
|
||||
*/
|
||||
getRange(nodeOrToken) {
|
||||
if (hasESTreeStyleRange(nodeOrToken)) {
|
||||
return nodeOrToken.range;
|
||||
}
|
||||
|
||||
if (hasPosStyleRange(nodeOrToken)) {
|
||||
return [
|
||||
nodeOrToken.position.start.offset,
|
||||
nodeOrToken.position.end.offset,
|
||||
];
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Custom getRange() method must be implemented in the subclass.",
|
||||
);
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars -- Required to complete interface. */
|
||||
/**
|
||||
* Returns the parent of the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node to get the parent of.
|
||||
* @returns {Options['SyntaxElementWithLoc']|undefined} The parent of the node.
|
||||
* @throws {Error} If the method is not implemented in the subclass.
|
||||
*/
|
||||
getParent(node) {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
/* eslint-enable no-unused-vars -- Required to complete interface. */
|
||||
|
||||
/**
|
||||
* Gets all the ancestors of a given node
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node
|
||||
* @returns {Array<Options['SyntaxElementWithLoc']>} All the ancestor nodes in the AST, not including the provided node, starting
|
||||
* from the root node at index 0 and going inwards to the parent node.
|
||||
* @throws {TypeError} When `node` is missing.
|
||||
*/
|
||||
getAncestors(node) {
|
||||
if (!node) {
|
||||
throw new TypeError("Missing required argument: node.");
|
||||
}
|
||||
|
||||
const ancestorsStartingAtParent = [];
|
||||
|
||||
for (
|
||||
let ancestor = this.getParent(node);
|
||||
ancestor;
|
||||
ancestor = this.getParent(ancestor)
|
||||
) {
|
||||
ancestorsStartingAtParent.push(ancestor);
|
||||
}
|
||||
|
||||
return ancestorsStartingAtParent.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source code for the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} [node] The AST node to get the text for.
|
||||
* @param {number} [beforeCount] The number of characters before the node to retrieve.
|
||||
* @param {number} [afterCount] The number of characters after the node to retrieve.
|
||||
* @returns {string} The text representing the AST node.
|
||||
* @public
|
||||
*/
|
||||
getText(node, beforeCount, afterCount) {
|
||||
if (node) {
|
||||
const range = this.getRange(node);
|
||||
return this.text.slice(
|
||||
Math.max(range[0] - (beforeCount || 0), 0),
|
||||
range[1] + (afterCount || 0),
|
||||
);
|
||||
}
|
||||
return this.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entire source text split into an array of lines.
|
||||
* @returns {Array<string>} The source text as an array of lines.
|
||||
* @public
|
||||
*/
|
||||
get lines() {
|
||||
this.#ensureLines(); // Ensure `#lines` is lazily calculated.
|
||||
|
||||
return this.#lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the source code and return the steps that were taken.
|
||||
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
|
||||
*/
|
||||
traverse() {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
exports.CallMethodStep = CallMethodStep;
|
||||
exports.ConfigCommentParser = ConfigCommentParser;
|
||||
exports.Directive = Directive;
|
||||
exports.TextSourceCodeBase = TextSourceCodeBase;
|
||||
exports.VisitNodeStep = VisitNodeStep;
|
||||
Generated
Vendored
+329
@@ -0,0 +1,329 @@
|
||||
export type VisitTraversalStep = $eslintcore.VisitTraversalStep;
|
||||
export type CallTraversalStep = $eslintcore.CallTraversalStep;
|
||||
export type TraversalStep = $eslintcore.TraversalStep;
|
||||
export type SourceLocation = $eslintcore.SourceLocation;
|
||||
export type SourceLocationWithOffset = $eslintcore.SourceLocationWithOffset;
|
||||
export type SourceRange = $eslintcore.SourceRange;
|
||||
export type IDirective = $eslintcore.Directive;
|
||||
export type DirectiveType = $eslintcore.DirectiveType;
|
||||
export type SourceCodeBaseTypeOptions = $eslintcore.SourceCodeBaseTypeOptions;
|
||||
export type TextSourceCode<Options extends SourceCodeBaseTypeOptions = $eslintcore.SourceCodeBaseTypeOptions> = import("@eslint/core").TextSourceCode<Options>;
|
||||
export type RuleVisitor = $eslintcore.RuleVisitor;
|
||||
export type CustomRuleVisitorWithExit<RuleVisitorType extends RuleVisitor> = import("./types.cts").CustomRuleVisitorWithExit<RuleVisitorType>;
|
||||
export type CustomRuleTypeDefinitions = $typests.CustomRuleTypeDefinitions;
|
||||
export type CustomRuleDefinitionType<LanguageSpecificOptions extends Omit<import("@eslint/core").RuleDefinitionTypeOptions, keyof CustomRuleTypeDefinitions>, Options extends Partial<CustomRuleTypeDefinitions>> = import("./types.cts").CustomRuleDefinitionType<LanguageSpecificOptions, Options>;
|
||||
export type RuleConfig = $eslintcore.RuleConfig;
|
||||
export type RulesConfig = $eslintcore.RulesConfig;
|
||||
export type StringConfig = $typests.StringConfig;
|
||||
export type BooleanConfig = $typests.BooleanConfig;
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a
|
||||
* method is called.
|
||||
* @implements {CallTraversalStep}
|
||||
*/
|
||||
export class CallMethodStep implements CallTraversalStep {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {string} options.target The target of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, args }: {
|
||||
target: string;
|
||||
args: Array<any>;
|
||||
});
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"call"}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: "call";
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {2}
|
||||
* @readonly
|
||||
*/
|
||||
readonly kind: 2;
|
||||
/**
|
||||
* The name of the method to call.
|
||||
* @type {string}
|
||||
*/
|
||||
target: string;
|
||||
/**
|
||||
* The arguments to pass to the method.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args: Array<any>;
|
||||
}
|
||||
/**
|
||||
* Object to parse ESLint configuration comments.
|
||||
*/
|
||||
export class ConfigCommentParser {
|
||||
/**
|
||||
* Parses a list of "name:string_value" or/and "name" options divided by comma or
|
||||
* whitespace. Used for "global" comments.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {StringConfig} Result map object of names and string values, or null values if no value was provided.
|
||||
*/
|
||||
parseStringConfig(string: string): StringConfig;
|
||||
/**
|
||||
* Parses a JSON-like config.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {({ok: true, config: RulesConfig}|{ok: false, error: {message: string}})} Result map object
|
||||
*/
|
||||
parseJSONLikeConfig(string: string): ({
|
||||
ok: true;
|
||||
config: RulesConfig;
|
||||
} | {
|
||||
ok: false;
|
||||
error: {
|
||||
message: string;
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Parses a config of values separated by comma.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {BooleanConfig} Result map of values and true values
|
||||
*/
|
||||
parseListConfig(string: string): BooleanConfig;
|
||||
/**
|
||||
* Parses a directive comment into directive text and value.
|
||||
* @param {string} string The string with the directive to be parsed.
|
||||
* @returns {DirectiveComment|undefined} The parsed directive or `undefined` if the directive is invalid.
|
||||
*/
|
||||
parseDirective(string: string): DirectiveComment | undefined;
|
||||
#private;
|
||||
}
|
||||
/**
|
||||
* A class to represent a directive comment.
|
||||
* @implements {IDirective}
|
||||
*/
|
||||
export class Directive implements IDirective {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the directive.
|
||||
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
|
||||
* @param {unknown} options.node The node representing the directive.
|
||||
* @param {string} options.value The value of the directive.
|
||||
* @param {string} options.justification The justification for the directive.
|
||||
*/
|
||||
constructor({ type, node, value, justification }: {
|
||||
type: "disable" | "enable" | "disable-next-line" | "disable-line";
|
||||
node: unknown;
|
||||
value: string;
|
||||
justification: string;
|
||||
});
|
||||
/**
|
||||
* The type of directive.
|
||||
* @type {DirectiveType}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: DirectiveType;
|
||||
/**
|
||||
* The node representing the directive.
|
||||
* @type {unknown}
|
||||
* @readonly
|
||||
*/
|
||||
readonly node: unknown;
|
||||
/**
|
||||
* Everything after the "eslint-disable" portion of the directive,
|
||||
* but before the "--" that indicates the justification.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
readonly value: string;
|
||||
/**
|
||||
* The justification for the directive.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
readonly justification: string;
|
||||
}
|
||||
/**
|
||||
* Source Code Base Object
|
||||
* @template {SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}} [Options=SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}]
|
||||
* @implements {TextSourceCode<Options>}
|
||||
*/
|
||||
export class TextSourceCodeBase<Options extends SourceCodeBaseTypeOptions & {
|
||||
RootNode: object;
|
||||
SyntaxElementWithLoc: object;
|
||||
} = $eslintcore.SourceCodeBaseTypeOptions & {
|
||||
RootNode: object;
|
||||
SyntaxElementWithLoc: object;
|
||||
}> implements TextSourceCode<Options> {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the instance.
|
||||
* @param {string} options.text The source code text.
|
||||
* @param {Options['RootNode']} options.ast The root AST node.
|
||||
* @param {RegExp} [options.lineEndingPattern] The pattern to match lineEndings in the source code. Defaults to `/\r?\n/u`.
|
||||
*/
|
||||
constructor({ text, ast, lineEndingPattern }: {
|
||||
text: string;
|
||||
ast: Options["RootNode"];
|
||||
lineEndingPattern?: RegExp;
|
||||
});
|
||||
/**
|
||||
* The AST of the source code.
|
||||
* @type {Options['RootNode']}
|
||||
*/
|
||||
ast: Options["RootNode"];
|
||||
/**
|
||||
* The text of the source code.
|
||||
* @type {string}
|
||||
*/
|
||||
text: string;
|
||||
/**
|
||||
* Returns the loc information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the loc information for.
|
||||
* @returns {SourceLocation} The loc information for the node or token.
|
||||
* @throws {Error} If the node or token does not have loc information.
|
||||
*/
|
||||
getLoc(nodeOrToken: Options["SyntaxElementWithLoc"]): SourceLocation;
|
||||
/**
|
||||
* Converts a source text index into a `{ line: number, column: number }` pair.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @throws {TypeError|RangeError} If non-numeric index or index out of range.
|
||||
* @returns {{line: number, column: number}} A `{ line: number, column: number }` location object with 0 or 1-indexed line and 0 or 1-indexed column based on language.
|
||||
* @public
|
||||
*/
|
||||
public getLocFromIndex(index: number): {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
/**
|
||||
* Converts a `{ line: number, column: number }` pair into a source text index.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} loc.column The column number of the location. (0 or 1-indexed based on language.)
|
||||
* @throws {TypeError|RangeError} If `loc` is not an object with a numeric
|
||||
* `line` and `column`, if the `line` is less than or equal to zero or
|
||||
* the `line` or `column` is out of the expected range.
|
||||
* @returns {number} The index of the line/column location in a file.
|
||||
* @public
|
||||
*/
|
||||
public getIndexFromLoc(loc: {
|
||||
line: number;
|
||||
column: number;
|
||||
}): number;
|
||||
/**
|
||||
* Returns the range information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the range information for.
|
||||
* @returns {SourceRange} The range information for the node or token.
|
||||
* @throws {Error} If the node or token does not have range information.
|
||||
*/
|
||||
getRange(nodeOrToken: Options["SyntaxElementWithLoc"]): SourceRange;
|
||||
/**
|
||||
* Returns the parent of the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node to get the parent of.
|
||||
* @returns {Options['SyntaxElementWithLoc']|undefined} The parent of the node.
|
||||
* @throws {Error} If the method is not implemented in the subclass.
|
||||
*/
|
||||
getParent(node: Options["SyntaxElementWithLoc"]): Options["SyntaxElementWithLoc"] | undefined;
|
||||
/**
|
||||
* Gets all the ancestors of a given node
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node
|
||||
* @returns {Array<Options['SyntaxElementWithLoc']>} All the ancestor nodes in the AST, not including the provided node, starting
|
||||
* from the root node at index 0 and going inwards to the parent node.
|
||||
* @throws {TypeError} When `node` is missing.
|
||||
*/
|
||||
getAncestors(node: Options["SyntaxElementWithLoc"]): Array<Options["SyntaxElementWithLoc"]>;
|
||||
/**
|
||||
* Gets the source code for the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} [node] The AST node to get the text for.
|
||||
* @param {number} [beforeCount] The number of characters before the node to retrieve.
|
||||
* @param {number} [afterCount] The number of characters after the node to retrieve.
|
||||
* @returns {string} The text representing the AST node.
|
||||
* @public
|
||||
*/
|
||||
public getText(node?: Options["SyntaxElementWithLoc"], beforeCount?: number, afterCount?: number): string;
|
||||
/**
|
||||
* Gets the entire source text split into an array of lines.
|
||||
* @returns {Array<string>} The source text as an array of lines.
|
||||
* @public
|
||||
*/
|
||||
public get lines(): Array<string>;
|
||||
/**
|
||||
* Traverse the source code and return the steps that were taken.
|
||||
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
|
||||
*/
|
||||
traverse(): Iterable<TraversalStep>;
|
||||
#private;
|
||||
}
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a node is visited.
|
||||
* @implements {VisitTraversalStep}
|
||||
*/
|
||||
export class VisitNodeStep implements VisitTraversalStep {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {object} options.target The target of the step.
|
||||
* @param {1|2} options.phase The phase of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, phase, args }: {
|
||||
target: object;
|
||||
phase: 1 | 2;
|
||||
args: Array<any>;
|
||||
});
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"visit"}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: "visit";
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {1}
|
||||
* @readonly
|
||||
*/
|
||||
readonly kind: 1;
|
||||
/**
|
||||
* The target of the step.
|
||||
* @type {object}
|
||||
*/
|
||||
target: object;
|
||||
/**
|
||||
* The phase of the step.
|
||||
* @type {1|2}
|
||||
*/
|
||||
phase: 1 | 2;
|
||||
/**
|
||||
* The arguments of the step.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args: Array<any>;
|
||||
}
|
||||
import type * as $eslintcore from "@eslint/core";
|
||||
import type * as $typests from "./types.cts";
|
||||
/**
|
||||
* Represents a directive comment.
|
||||
*/
|
||||
declare class DirectiveComment {
|
||||
/**
|
||||
* Creates a new directive comment.
|
||||
* @param {string} label The label of the directive.
|
||||
* @param {string} value The value of the directive.
|
||||
* @param {string} justification The justification of the directive.
|
||||
*/
|
||||
constructor(label: string, value: string, justification: string);
|
||||
/**
|
||||
* The label of the directive, such as "eslint", "eslint-disable", etc.
|
||||
* @type {string}
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The value of the directive (the string after the label).
|
||||
* @type {string}
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The justification of the directive (the string after the --).
|
||||
* @type {string}
|
||||
*/
|
||||
justification: string;
|
||||
}
|
||||
export {};
|
||||
Generated
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @fileoverview Types for the plugin-kit package.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Imports
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
import type {
|
||||
RuleDefinition,
|
||||
RuleDefinitionTypeOptions,
|
||||
RuleVisitor,
|
||||
} from "@eslint/core";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Exports
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Defaults for non-language-related `RuleDefinition` options.
|
||||
*/
|
||||
export interface CustomRuleTypeDefinitions {
|
||||
RuleOptions: unknown[];
|
||||
MessageIds: string;
|
||||
ExtRuleDocs: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper type to define language specific specializations of the `RuleDefinition` type.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type YourRuleDefinition<
|
||||
* Options extends Partial<CustomRuleTypeDefinitions> = {},
|
||||
* > = CustomRuleDefinitionType<
|
||||
* {
|
||||
* LangOptions: YourLanguageOptions;
|
||||
* Code: YourSourceCode;
|
||||
* Visitor: YourRuleVisitor;
|
||||
* Node: YourNode;
|
||||
* },
|
||||
* Options
|
||||
* >;
|
||||
* ```
|
||||
*/
|
||||
export type CustomRuleDefinitionType<
|
||||
LanguageSpecificOptions extends Omit<
|
||||
RuleDefinitionTypeOptions,
|
||||
keyof CustomRuleTypeDefinitions
|
||||
>,
|
||||
Options extends Partial<CustomRuleTypeDefinitions>,
|
||||
> = RuleDefinition<
|
||||
// Language specific type options (non-configurable)
|
||||
LanguageSpecificOptions &
|
||||
Required<
|
||||
// Rule specific type options (custom)
|
||||
Options &
|
||||
// Rule specific type options (defaults)
|
||||
Omit<CustomRuleTypeDefinitions, keyof Options>
|
||||
>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Adds matching `:exit` selector properties for each key of a `RuleVisitor`.
|
||||
*/
|
||||
export type CustomRuleVisitorWithExit<RuleVisitorType extends RuleVisitor> = {
|
||||
[Key in keyof RuleVisitorType as
|
||||
| Key
|
||||
| `${Key & string}:exit`]: RuleVisitorType[Key];
|
||||
};
|
||||
|
||||
/**
|
||||
* A map of names to string values, or `null` when no value is provided.
|
||||
*/
|
||||
export type StringConfig = Record<string, string | null>;
|
||||
|
||||
/**
|
||||
* A map of names to boolean flags.
|
||||
*/
|
||||
export type BooleanConfig = Record<string, boolean>;
|
||||
Generated
Vendored
+329
@@ -0,0 +1,329 @@
|
||||
export type VisitTraversalStep = $eslintcore.VisitTraversalStep;
|
||||
export type CallTraversalStep = $eslintcore.CallTraversalStep;
|
||||
export type TraversalStep = $eslintcore.TraversalStep;
|
||||
export type SourceLocation = $eslintcore.SourceLocation;
|
||||
export type SourceLocationWithOffset = $eslintcore.SourceLocationWithOffset;
|
||||
export type SourceRange = $eslintcore.SourceRange;
|
||||
export type IDirective = $eslintcore.Directive;
|
||||
export type DirectiveType = $eslintcore.DirectiveType;
|
||||
export type SourceCodeBaseTypeOptions = $eslintcore.SourceCodeBaseTypeOptions;
|
||||
export type TextSourceCode<Options extends SourceCodeBaseTypeOptions = $eslintcore.SourceCodeBaseTypeOptions> = import("@eslint/core").TextSourceCode<Options>;
|
||||
export type RuleVisitor = $eslintcore.RuleVisitor;
|
||||
export type CustomRuleVisitorWithExit<RuleVisitorType extends RuleVisitor> = import("./types.ts").CustomRuleVisitorWithExit<RuleVisitorType>;
|
||||
export type CustomRuleTypeDefinitions = $typests.CustomRuleTypeDefinitions;
|
||||
export type CustomRuleDefinitionType<LanguageSpecificOptions extends Omit<import("@eslint/core").RuleDefinitionTypeOptions, keyof CustomRuleTypeDefinitions>, Options extends Partial<CustomRuleTypeDefinitions>> = import("./types.ts").CustomRuleDefinitionType<LanguageSpecificOptions, Options>;
|
||||
export type RuleConfig = $eslintcore.RuleConfig;
|
||||
export type RulesConfig = $eslintcore.RulesConfig;
|
||||
export type StringConfig = $typests.StringConfig;
|
||||
export type BooleanConfig = $typests.BooleanConfig;
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a
|
||||
* method is called.
|
||||
* @implements {CallTraversalStep}
|
||||
*/
|
||||
export class CallMethodStep implements CallTraversalStep {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {string} options.target The target of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, args }: {
|
||||
target: string;
|
||||
args: Array<any>;
|
||||
});
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"call"}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: "call";
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {2}
|
||||
* @readonly
|
||||
*/
|
||||
readonly kind: 2;
|
||||
/**
|
||||
* The name of the method to call.
|
||||
* @type {string}
|
||||
*/
|
||||
target: string;
|
||||
/**
|
||||
* The arguments to pass to the method.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args: Array<any>;
|
||||
}
|
||||
/**
|
||||
* Object to parse ESLint configuration comments.
|
||||
*/
|
||||
export class ConfigCommentParser {
|
||||
/**
|
||||
* Parses a list of "name:string_value" or/and "name" options divided by comma or
|
||||
* whitespace. Used for "global" comments.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {StringConfig} Result map object of names and string values, or null values if no value was provided.
|
||||
*/
|
||||
parseStringConfig(string: string): StringConfig;
|
||||
/**
|
||||
* Parses a JSON-like config.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {({ok: true, config: RulesConfig}|{ok: false, error: {message: string}})} Result map object
|
||||
*/
|
||||
parseJSONLikeConfig(string: string): ({
|
||||
ok: true;
|
||||
config: RulesConfig;
|
||||
} | {
|
||||
ok: false;
|
||||
error: {
|
||||
message: string;
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Parses a config of values separated by comma.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {BooleanConfig} Result map of values and true values
|
||||
*/
|
||||
parseListConfig(string: string): BooleanConfig;
|
||||
/**
|
||||
* Parses a directive comment into directive text and value.
|
||||
* @param {string} string The string with the directive to be parsed.
|
||||
* @returns {DirectiveComment|undefined} The parsed directive or `undefined` if the directive is invalid.
|
||||
*/
|
||||
parseDirective(string: string): DirectiveComment | undefined;
|
||||
#private;
|
||||
}
|
||||
/**
|
||||
* A class to represent a directive comment.
|
||||
* @implements {IDirective}
|
||||
*/
|
||||
export class Directive implements IDirective {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the directive.
|
||||
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
|
||||
* @param {unknown} options.node The node representing the directive.
|
||||
* @param {string} options.value The value of the directive.
|
||||
* @param {string} options.justification The justification for the directive.
|
||||
*/
|
||||
constructor({ type, node, value, justification }: {
|
||||
type: "disable" | "enable" | "disable-next-line" | "disable-line";
|
||||
node: unknown;
|
||||
value: string;
|
||||
justification: string;
|
||||
});
|
||||
/**
|
||||
* The type of directive.
|
||||
* @type {DirectiveType}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: DirectiveType;
|
||||
/**
|
||||
* The node representing the directive.
|
||||
* @type {unknown}
|
||||
* @readonly
|
||||
*/
|
||||
readonly node: unknown;
|
||||
/**
|
||||
* Everything after the "eslint-disable" portion of the directive,
|
||||
* but before the "--" that indicates the justification.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
readonly value: string;
|
||||
/**
|
||||
* The justification for the directive.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
readonly justification: string;
|
||||
}
|
||||
/**
|
||||
* Source Code Base Object
|
||||
* @template {SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}} [Options=SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}]
|
||||
* @implements {TextSourceCode<Options>}
|
||||
*/
|
||||
export class TextSourceCodeBase<Options extends SourceCodeBaseTypeOptions & {
|
||||
RootNode: object;
|
||||
SyntaxElementWithLoc: object;
|
||||
} = $eslintcore.SourceCodeBaseTypeOptions & {
|
||||
RootNode: object;
|
||||
SyntaxElementWithLoc: object;
|
||||
}> implements TextSourceCode<Options> {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the instance.
|
||||
* @param {string} options.text The source code text.
|
||||
* @param {Options['RootNode']} options.ast The root AST node.
|
||||
* @param {RegExp} [options.lineEndingPattern] The pattern to match lineEndings in the source code. Defaults to `/\r?\n/u`.
|
||||
*/
|
||||
constructor({ text, ast, lineEndingPattern }: {
|
||||
text: string;
|
||||
ast: Options["RootNode"];
|
||||
lineEndingPattern?: RegExp;
|
||||
});
|
||||
/**
|
||||
* The AST of the source code.
|
||||
* @type {Options['RootNode']}
|
||||
*/
|
||||
ast: Options["RootNode"];
|
||||
/**
|
||||
* The text of the source code.
|
||||
* @type {string}
|
||||
*/
|
||||
text: string;
|
||||
/**
|
||||
* Returns the loc information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the loc information for.
|
||||
* @returns {SourceLocation} The loc information for the node or token.
|
||||
* @throws {Error} If the node or token does not have loc information.
|
||||
*/
|
||||
getLoc(nodeOrToken: Options["SyntaxElementWithLoc"]): SourceLocation;
|
||||
/**
|
||||
* Converts a source text index into a `{ line: number, column: number }` pair.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @throws {TypeError|RangeError} If non-numeric index or index out of range.
|
||||
* @returns {{line: number, column: number}} A `{ line: number, column: number }` location object with 0 or 1-indexed line and 0 or 1-indexed column based on language.
|
||||
* @public
|
||||
*/
|
||||
public getLocFromIndex(index: number): {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
/**
|
||||
* Converts a `{ line: number, column: number }` pair into a source text index.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} loc.column The column number of the location. (0 or 1-indexed based on language.)
|
||||
* @throws {TypeError|RangeError} If `loc` is not an object with a numeric
|
||||
* `line` and `column`, if the `line` is less than or equal to zero or
|
||||
* the `line` or `column` is out of the expected range.
|
||||
* @returns {number} The index of the line/column location in a file.
|
||||
* @public
|
||||
*/
|
||||
public getIndexFromLoc(loc: {
|
||||
line: number;
|
||||
column: number;
|
||||
}): number;
|
||||
/**
|
||||
* Returns the range information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the range information for.
|
||||
* @returns {SourceRange} The range information for the node or token.
|
||||
* @throws {Error} If the node or token does not have range information.
|
||||
*/
|
||||
getRange(nodeOrToken: Options["SyntaxElementWithLoc"]): SourceRange;
|
||||
/**
|
||||
* Returns the parent of the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node to get the parent of.
|
||||
* @returns {Options['SyntaxElementWithLoc']|undefined} The parent of the node.
|
||||
* @throws {Error} If the method is not implemented in the subclass.
|
||||
*/
|
||||
getParent(node: Options["SyntaxElementWithLoc"]): Options["SyntaxElementWithLoc"] | undefined;
|
||||
/**
|
||||
* Gets all the ancestors of a given node
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node
|
||||
* @returns {Array<Options['SyntaxElementWithLoc']>} All the ancestor nodes in the AST, not including the provided node, starting
|
||||
* from the root node at index 0 and going inwards to the parent node.
|
||||
* @throws {TypeError} When `node` is missing.
|
||||
*/
|
||||
getAncestors(node: Options["SyntaxElementWithLoc"]): Array<Options["SyntaxElementWithLoc"]>;
|
||||
/**
|
||||
* Gets the source code for the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} [node] The AST node to get the text for.
|
||||
* @param {number} [beforeCount] The number of characters before the node to retrieve.
|
||||
* @param {number} [afterCount] The number of characters after the node to retrieve.
|
||||
* @returns {string} The text representing the AST node.
|
||||
* @public
|
||||
*/
|
||||
public getText(node?: Options["SyntaxElementWithLoc"], beforeCount?: number, afterCount?: number): string;
|
||||
/**
|
||||
* Gets the entire source text split into an array of lines.
|
||||
* @returns {Array<string>} The source text as an array of lines.
|
||||
* @public
|
||||
*/
|
||||
public get lines(): Array<string>;
|
||||
/**
|
||||
* Traverse the source code and return the steps that were taken.
|
||||
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
|
||||
*/
|
||||
traverse(): Iterable<TraversalStep>;
|
||||
#private;
|
||||
}
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a node is visited.
|
||||
* @implements {VisitTraversalStep}
|
||||
*/
|
||||
export class VisitNodeStep implements VisitTraversalStep {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {object} options.target The target of the step.
|
||||
* @param {1|2} options.phase The phase of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, phase, args }: {
|
||||
target: object;
|
||||
phase: 1 | 2;
|
||||
args: Array<any>;
|
||||
});
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"visit"}
|
||||
* @readonly
|
||||
*/
|
||||
readonly type: "visit";
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {1}
|
||||
* @readonly
|
||||
*/
|
||||
readonly kind: 1;
|
||||
/**
|
||||
* The target of the step.
|
||||
* @type {object}
|
||||
*/
|
||||
target: object;
|
||||
/**
|
||||
* The phase of the step.
|
||||
* @type {1|2}
|
||||
*/
|
||||
phase: 1 | 2;
|
||||
/**
|
||||
* The arguments of the step.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args: Array<any>;
|
||||
}
|
||||
import type * as $eslintcore from "@eslint/core";
|
||||
import type * as $typests from "./types.ts";
|
||||
/**
|
||||
* Represents a directive comment.
|
||||
*/
|
||||
declare class DirectiveComment {
|
||||
/**
|
||||
* Creates a new directive comment.
|
||||
* @param {string} label The label of the directive.
|
||||
* @param {string} value The value of the directive.
|
||||
* @param {string} justification The justification of the directive.
|
||||
*/
|
||||
constructor(label: string, value: string, justification: string);
|
||||
/**
|
||||
* The label of the directive, such as "eslint", "eslint-disable", etc.
|
||||
* @type {string}
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The value of the directive (the string after the label).
|
||||
* @type {string}
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The justification of the directive (the string after the --).
|
||||
* @type {string}
|
||||
*/
|
||||
justification: string;
|
||||
}
|
||||
export {};
|
||||
Generated
Vendored
+890
@@ -0,0 +1,890 @@
|
||||
// @ts-self-types="./index.d.ts"
|
||||
import levn from 'levn';
|
||||
|
||||
/**
|
||||
* @fileoverview Config Comment Parser
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @import * as $eslintcore from "@eslint/core"; */
|
||||
/** @typedef {$eslintcore.RuleConfig} RuleConfig */
|
||||
/** @typedef {$eslintcore.RulesConfig} RulesConfig */
|
||||
/** @import * as $typests from "./types.ts"; */
|
||||
/** @typedef {$typests.StringConfig} StringConfig */
|
||||
/** @typedef {$typests.BooleanConfig} BooleanConfig */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const directivesPattern = /^([a-z]+(?:-[a-z]+)*)(?:\s|$)/u;
|
||||
const validSeverities = new Set([0, 1, 2, "off", "warn", "error"]);
|
||||
|
||||
/**
|
||||
* Determines if the severity in the rule configuration is valid.
|
||||
* @param {RuleConfig} ruleConfig A rule's configuration.
|
||||
* @returns {boolean} `true` if the severity is valid, otherwise `false`.
|
||||
*/
|
||||
function isSeverityValid(ruleConfig) {
|
||||
const severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
|
||||
return validSeverities.has(severity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if all severities in the rules configuration are valid.
|
||||
* @param {RulesConfig} rulesConfig The rules configuration to check.
|
||||
* @returns {boolean} `true` if all severities are valid, otherwise `false`.
|
||||
*/
|
||||
function isEverySeverityValid(rulesConfig) {
|
||||
return Object.values(rulesConfig).every(isSeverityValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a directive comment.
|
||||
*/
|
||||
class DirectiveComment {
|
||||
/**
|
||||
* The label of the directive, such as "eslint", "eslint-disable", etc.
|
||||
* @type {string}
|
||||
*/
|
||||
label = "";
|
||||
|
||||
/**
|
||||
* The value of the directive (the string after the label).
|
||||
* @type {string}
|
||||
*/
|
||||
value = "";
|
||||
|
||||
/**
|
||||
* The justification of the directive (the string after the --).
|
||||
* @type {string}
|
||||
*/
|
||||
justification = "";
|
||||
|
||||
/**
|
||||
* Creates a new directive comment.
|
||||
* @param {string} label The label of the directive.
|
||||
* @param {string} value The value of the directive.
|
||||
* @param {string} justification The justification of the directive.
|
||||
*/
|
||||
constructor(label, value, justification) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
this.justification = justification;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Object to parse ESLint configuration comments.
|
||||
*/
|
||||
class ConfigCommentParser {
|
||||
/**
|
||||
* Parses a list of "name:string_value" or/and "name" options divided by comma or
|
||||
* whitespace. Used for "global" comments.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {StringConfig} Result map object of names and string values, or null values if no value was provided.
|
||||
*/
|
||||
parseStringConfig(string) {
|
||||
const items = /** @type {StringConfig} */ ({});
|
||||
|
||||
// Collapse whitespace around `:` and `,` to make parsing easier
|
||||
const trimmedString = string
|
||||
.trim()
|
||||
.replace(/(?<!\s)\s*([:,])\s*/gu, "$1");
|
||||
|
||||
trimmedString.split(/\s|,+/u).forEach(name => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// value defaults to null (if not provided), e.g: "foo" => ["foo", null]
|
||||
const [key, value = null] = name.split(":");
|
||||
|
||||
items[key] = value;
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON-like config.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {({ok: true, config: RulesConfig}|{ok: false, error: {message: string}})} Result map object
|
||||
*/
|
||||
parseJSONLikeConfig(string) {
|
||||
// Parses a JSON-like comment by the same way as parsing CLI option.
|
||||
try {
|
||||
const items =
|
||||
/** @type {RulesConfig} */ (levn.parse("Object", string)) || {};
|
||||
|
||||
/*
|
||||
* When the configuration has any invalid severities, it should be completely
|
||||
* ignored. This is because the configuration is not valid and should not be
|
||||
* applied.
|
||||
*
|
||||
* For example, the following configuration is invalid:
|
||||
*
|
||||
* "no-alert: 2 no-console: 2"
|
||||
*
|
||||
* This results in a configuration of { "no-alert": "2 no-console: 2" }, which is
|
||||
* not valid. In this case, the configuration should be ignored.
|
||||
*/
|
||||
if (isEverySeverityValid(items)) {
|
||||
return {
|
||||
ok: true,
|
||||
config: items,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// levn parsing error: ignore to parse the string by a fallback.
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionator cannot parse commaless notations.
|
||||
* But we are supporting that. So this is a fallback for that.
|
||||
*/
|
||||
const normalizedString = string
|
||||
.replace(/(?<![-a-zA-Z0-9/])([-a-zA-Z0-9/]+):/gu, '"$1":')
|
||||
.replace(/([\]0-9])\s+(?=")/u, "$1,");
|
||||
|
||||
try {
|
||||
const items = JSON.parse(`{${normalizedString}}`);
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
config: items,
|
||||
};
|
||||
} catch (ex) {
|
||||
const errorMessage = ex instanceof Error ? ex.message : String(ex);
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
error: {
|
||||
message: `Failed to parse JSON from '${normalizedString}': ${errorMessage}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a config of values separated by comma.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {BooleanConfig} Result map of values and true values
|
||||
*/
|
||||
parseListConfig(string) {
|
||||
const items = /** @type {BooleanConfig} */ ({});
|
||||
|
||||
string.split(",").forEach(name => {
|
||||
const trimmedName = name
|
||||
.trim()
|
||||
.replace(
|
||||
/^(?<quote>['"]?)(?<ruleId>.*)\k<quote>$/su,
|
||||
"$<ruleId>",
|
||||
);
|
||||
|
||||
if (trimmedName) {
|
||||
items[trimmedName] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the directive and the justification from a given directive comment and trim them.
|
||||
* @param {string} value The comment text to extract.
|
||||
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
|
||||
*/
|
||||
#extractDirectiveComment(value) {
|
||||
const match = /\s-{2,}\s/u.exec(value);
|
||||
|
||||
if (!match) {
|
||||
return { directivePart: value.trim(), justificationPart: "" };
|
||||
}
|
||||
|
||||
const directive = value.slice(0, match.index).trim();
|
||||
const justification = value.slice(match.index + match[0].length).trim();
|
||||
|
||||
return { directivePart: directive, justificationPart: justification };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a directive comment into directive text and value.
|
||||
* @param {string} string The string with the directive to be parsed.
|
||||
* @returns {DirectiveComment|undefined} The parsed directive or `undefined` if the directive is invalid.
|
||||
*/
|
||||
parseDirective(string) {
|
||||
const { directivePart, justificationPart } =
|
||||
this.#extractDirectiveComment(string);
|
||||
const match = directivesPattern.exec(directivePart);
|
||||
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const directiveText = match[1];
|
||||
const directiveValue = directivePart.slice(
|
||||
match.index + directiveText.length,
|
||||
);
|
||||
|
||||
return new DirectiveComment(
|
||||
directiveText,
|
||||
directiveValue.trim(),
|
||||
justificationPart,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @fileoverview A collection of helper classes for implementing `SourceCode`.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
/* eslint class-methods-use-this: off -- Required to complete interface. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {$eslintcore.VisitTraversalStep} VisitTraversalStep */
|
||||
/** @typedef {$eslintcore.CallTraversalStep} CallTraversalStep */
|
||||
/** @typedef {$eslintcore.TraversalStep} TraversalStep */
|
||||
/** @typedef {$eslintcore.SourceLocation} SourceLocation */
|
||||
/** @typedef {$eslintcore.SourceLocationWithOffset} SourceLocationWithOffset */
|
||||
/** @typedef {$eslintcore.SourceRange} SourceRange */
|
||||
/** @typedef {$eslintcore.Directive} IDirective */
|
||||
/** @typedef {$eslintcore.DirectiveType} DirectiveType */
|
||||
/** @typedef {$eslintcore.SourceCodeBaseTypeOptions} SourceCodeBaseTypeOptions */
|
||||
/**
|
||||
* @typedef {import("@eslint/core").TextSourceCode<Options>} TextSourceCode
|
||||
* @template {SourceCodeBaseTypeOptions} [Options=SourceCodeBaseTypeOptions]
|
||||
*/
|
||||
/** @typedef {$eslintcore.RuleVisitor} RuleVisitor */
|
||||
/**
|
||||
* @typedef {import("./types.ts").CustomRuleVisitorWithExit<RuleVisitorType>} CustomRuleVisitorWithExit
|
||||
* @template {RuleVisitor} RuleVisitorType
|
||||
*/
|
||||
/** @typedef {$typests.CustomRuleTypeDefinitions} CustomRuleTypeDefinitions */
|
||||
/**
|
||||
* @typedef {import("./types.ts").CustomRuleDefinitionType<LanguageSpecificOptions, Options>} CustomRuleDefinitionType
|
||||
* @template {Omit<import("@eslint/core").RuleDefinitionTypeOptions, keyof CustomRuleTypeDefinitions>} LanguageSpecificOptions
|
||||
* @template {Partial<CustomRuleTypeDefinitions>} Options
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if a node has ESTree-style loc information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {loc:SourceLocation}} `true` if the node has ESTree-style loc information, `false` if not.
|
||||
*/
|
||||
function hasESTreeStyleLoc(node) {
|
||||
return "loc" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has position-style loc information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {position:SourceLocation}} `true` if the node has position-style range information, `false` if not.
|
||||
*/
|
||||
function hasPosStyleLoc(node) {
|
||||
return "position" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has ESTree-style range information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {range:SourceRange}} `true` if the node has ESTree-style range information, `false` if not.
|
||||
*/
|
||||
function hasESTreeStyleRange(node) {
|
||||
return "range" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node has position-style range information.
|
||||
* @param {object} node The node to check.
|
||||
* @returns {node is {position:SourceLocationWithOffset}} `true` if the node has position-style range information, `false` if not.
|
||||
*/
|
||||
function hasPosStyleRange(node) {
|
||||
return "position" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs binary search to find the line number containing a given target index.
|
||||
* Returns the lower bound - the index of the first element greater than the target.
|
||||
* **Please note that the `lineStartIndices` should be sorted in ascending order**.
|
||||
* - Time Complexity: O(log n) - Significantly faster than linear search for large files.
|
||||
* @param {number[]} lineStartIndices Sorted array of line start indices.
|
||||
* @param {number} targetIndex The target index to find the line number for.
|
||||
* @returns {number} The line number for the target index.
|
||||
*/
|
||||
function findLineNumberBinarySearch(lineStartIndices, targetIndex) {
|
||||
let low = 0;
|
||||
let high = lineStartIndices.length - 1;
|
||||
|
||||
while (low < high) {
|
||||
const mid = ((low + high) / 2) | 0; // Use bitwise OR to floor the division.
|
||||
|
||||
if (targetIndex < lineStartIndices[mid]) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a node is visited.
|
||||
* @implements {VisitTraversalStep}
|
||||
*/
|
||||
class VisitNodeStep {
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"visit"}
|
||||
* @readonly
|
||||
*/
|
||||
type = "visit";
|
||||
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {1}
|
||||
* @readonly
|
||||
*/
|
||||
kind = 1;
|
||||
|
||||
/**
|
||||
* The target of the step.
|
||||
* @type {object}
|
||||
*/
|
||||
target;
|
||||
|
||||
/**
|
||||
* The phase of the step.
|
||||
* @type {1|2}
|
||||
*/
|
||||
phase;
|
||||
|
||||
/**
|
||||
* The arguments of the step.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {object} options.target The target of the step.
|
||||
* @param {1|2} options.phase The phase of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, phase, args }) {
|
||||
this.target = target;
|
||||
this.phase = phase;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to represent a step in the traversal process where a
|
||||
* method is called.
|
||||
* @implements {CallTraversalStep}
|
||||
*/
|
||||
class CallMethodStep {
|
||||
/**
|
||||
* The type of the step.
|
||||
* @type {"call"}
|
||||
* @readonly
|
||||
*/
|
||||
type = "call";
|
||||
|
||||
/**
|
||||
* The kind of the step. Represents the same data as the `type` property
|
||||
* but it's a number for performance.
|
||||
* @type {2}
|
||||
* @readonly
|
||||
*/
|
||||
kind = 2;
|
||||
|
||||
/**
|
||||
* The name of the method to call.
|
||||
* @type {string}
|
||||
*/
|
||||
target;
|
||||
|
||||
/**
|
||||
* The arguments to pass to the method.
|
||||
* @type {Array<any>}
|
||||
*/
|
||||
args;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the step.
|
||||
* @param {string} options.target The target of the step.
|
||||
* @param {Array<any>} options.args The arguments of the step.
|
||||
*/
|
||||
constructor({ target, args }) {
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to represent a directive comment.
|
||||
* @implements {IDirective}
|
||||
*/
|
||||
class Directive {
|
||||
/**
|
||||
* The type of directive.
|
||||
* @type {DirectiveType}
|
||||
* @readonly
|
||||
*/
|
||||
type;
|
||||
|
||||
/**
|
||||
* The node representing the directive.
|
||||
* @type {unknown}
|
||||
* @readonly
|
||||
*/
|
||||
node;
|
||||
|
||||
/**
|
||||
* Everything after the "eslint-disable" portion of the directive,
|
||||
* but before the "--" that indicates the justification.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
value;
|
||||
|
||||
/**
|
||||
* The justification for the directive.
|
||||
* @type {string}
|
||||
* @readonly
|
||||
*/
|
||||
justification;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the directive.
|
||||
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
|
||||
* @param {unknown} options.node The node representing the directive.
|
||||
* @param {string} options.value The value of the directive.
|
||||
* @param {string} options.justification The justification for the directive.
|
||||
*/
|
||||
constructor({ type, node, value, justification }) {
|
||||
this.type = type;
|
||||
this.node = node;
|
||||
this.value = value;
|
||||
this.justification = justification;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source Code Base Object
|
||||
* @template {SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}} [Options=SourceCodeBaseTypeOptions & {RootNode: object, SyntaxElementWithLoc: object}]
|
||||
* @implements {TextSourceCode<Options>}
|
||||
*/
|
||||
class TextSourceCodeBase {
|
||||
/**
|
||||
* The lines of text in the source code.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
#lines = [];
|
||||
|
||||
/**
|
||||
* The indices of the start of each line in the source code.
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
#lineStartIndices = [0];
|
||||
|
||||
/**
|
||||
* The pattern to match lineEndings in the source code.
|
||||
* @type {RegExp}
|
||||
*/
|
||||
#lineEndingPattern;
|
||||
|
||||
/**
|
||||
* The AST of the source code.
|
||||
* @type {Options['RootNode']}
|
||||
*/
|
||||
ast;
|
||||
|
||||
/**
|
||||
* The text of the source code.
|
||||
* @type {string}
|
||||
*/
|
||||
text;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options The options for the instance.
|
||||
* @param {string} options.text The source code text.
|
||||
* @param {Options['RootNode']} options.ast The root AST node.
|
||||
* @param {RegExp} [options.lineEndingPattern] The pattern to match lineEndings in the source code. Defaults to `/\r?\n/u`.
|
||||
*/
|
||||
constructor({ text, ast, lineEndingPattern = /\r?\n/u }) {
|
||||
this.ast = ast;
|
||||
this.text = text;
|
||||
// Remove the global(`g`) and sticky(`y`) flags from the `lineEndingPattern` to avoid issues with lastIndex.
|
||||
this.#lineEndingPattern = new RegExp(
|
||||
lineEndingPattern.source,
|
||||
lineEndingPattern.flags.replace(/[gy]/gu, ""),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the next line in the source text and updates `#lines` and `#lineStartIndices`.
|
||||
* @param {string} text The text to search for the next line.
|
||||
* @returns {boolean} `true` if a next line was found, `false` otherwise.
|
||||
*/
|
||||
#findNextLine(text) {
|
||||
const match = this.#lineEndingPattern.exec(text);
|
||||
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.#lines.push(text.slice(0, match.index));
|
||||
this.#lineStartIndices.push(
|
||||
(this.#lineStartIndices.at(-1) ?? 0) +
|
||||
match.index +
|
||||
match[0].length,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lines` is lazily calculated from the source text.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLines() {
|
||||
// If `#lines` has already been calculated, do nothing.
|
||||
if (this.#lines.length === this.#lineStartIndices.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found.
|
||||
}
|
||||
|
||||
this.#lines.push(this.text.slice(this.#lineStartIndices.at(-1)));
|
||||
|
||||
Object.freeze(this.#lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lineStartIndices` is lazily calculated up to the specified index.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLineStartIndicesFromIndex(index) {
|
||||
// If we've already parsed up to or beyond this index, do nothing.
|
||||
if (index <= (this.#lineStartIndices.at(-1) ?? 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
index > (this.#lineStartIndices.at(-1) ?? 0) &&
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures `#lineStartIndices` is lazily calculated up to the specified loc.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} lineStart The line number at which the parser starts counting.
|
||||
* @returns {void}
|
||||
*/
|
||||
#ensureLineStartIndicesFromLoc(loc, lineStart) {
|
||||
// Calculate line indices up to the potentially next line, as it is needed for the follow‑up calculation.
|
||||
const nextLocLineIndex = loc.line - lineStart + 1;
|
||||
const lastCalculatedLineIndex = this.#lineStartIndices.length - 1;
|
||||
let additionalLinesNeeded = nextLocLineIndex - lastCalculatedLineIndex;
|
||||
|
||||
// If we've already parsed up to or beyond this line, do nothing.
|
||||
if (additionalLinesNeeded <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (
|
||||
additionalLinesNeeded > 0 &&
|
||||
this.#findNextLine(this.text.slice(this.#lineStartIndices.at(-1)))
|
||||
) {
|
||||
// Continue parsing until no more matches are found or we have enough lines.
|
||||
additionalLinesNeeded -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loc information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the loc information for.
|
||||
* @returns {SourceLocation} The loc information for the node or token.
|
||||
* @throws {Error} If the node or token does not have loc information.
|
||||
*/
|
||||
getLoc(nodeOrToken) {
|
||||
if (hasESTreeStyleLoc(nodeOrToken)) {
|
||||
return nodeOrToken.loc;
|
||||
}
|
||||
|
||||
if (hasPosStyleLoc(nodeOrToken)) {
|
||||
return nodeOrToken.position;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Custom getLoc() method must be implemented in the subclass.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a source text index into a `{ line: number, column: number }` pair.
|
||||
* @param {number} index The index of a character in a file.
|
||||
* @throws {TypeError|RangeError} If non-numeric index or index out of range.
|
||||
* @returns {{line: number, column: number}} A `{ line: number, column: number }` location object with 0 or 1-indexed line and 0 or 1-indexed column based on language.
|
||||
* @public
|
||||
*/
|
||||
getLocFromIndex(index) {
|
||||
if (typeof index !== "number") {
|
||||
throw new TypeError("Expected `index` to be a number.");
|
||||
}
|
||||
|
||||
if (index < 0 || index > this.text.length) {
|
||||
throw new RangeError(
|
||||
`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`,
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
start: { line: lineStart, column: columnStart },
|
||||
end: { line: lineEnd, column: columnEnd },
|
||||
} = this.getLoc(this.ast);
|
||||
|
||||
// If the index is at the start, return the start location of the root node.
|
||||
if (index === 0) {
|
||||
return {
|
||||
line: lineStart,
|
||||
column: columnStart,
|
||||
};
|
||||
}
|
||||
|
||||
// If the index is `this.text.length`, return the location one "spot" past the last character of the file.
|
||||
if (index === this.text.length) {
|
||||
return {
|
||||
line: lineEnd,
|
||||
column: columnEnd,
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure `#lineStartIndices` are lazily calculated.
|
||||
this.#ensureLineStartIndicesFromIndex(index);
|
||||
|
||||
/*
|
||||
* To figure out which line `index` is on, determine the last place at which index could
|
||||
* be inserted into `#lineStartIndices` to keep the list sorted.
|
||||
*/
|
||||
const lineNumber =
|
||||
(index >= (this.#lineStartIndices.at(-1) ?? 0)
|
||||
? this.#lineStartIndices.length
|
||||
: findLineNumberBinarySearch(this.#lineStartIndices, index)) -
|
||||
1 +
|
||||
lineStart;
|
||||
|
||||
return {
|
||||
line: lineNumber,
|
||||
column:
|
||||
index -
|
||||
this.#lineStartIndices[lineNumber - lineStart] +
|
||||
columnStart,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a `{ line: number, column: number }` pair into a source text index.
|
||||
* @param {Object} loc A line/column location.
|
||||
* @param {number} loc.line The line number of the location. (0 or 1-indexed based on language.)
|
||||
* @param {number} loc.column The column number of the location. (0 or 1-indexed based on language.)
|
||||
* @throws {TypeError|RangeError} If `loc` is not an object with a numeric
|
||||
* `line` and `column`, if the `line` is less than or equal to zero or
|
||||
* the `line` or `column` is out of the expected range.
|
||||
* @returns {number} The index of the line/column location in a file.
|
||||
* @public
|
||||
*/
|
||||
getIndexFromLoc(loc) {
|
||||
if (
|
||||
loc === null ||
|
||||
typeof loc !== "object" ||
|
||||
typeof loc.line !== "number" ||
|
||||
typeof loc.column !== "number"
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Expected `loc` to be an object with numeric `line` and `column` properties.",
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
start: { line: lineStart, column: columnStart },
|
||||
end: { line: lineEnd, column: columnEnd },
|
||||
} = this.getLoc(this.ast);
|
||||
|
||||
if (loc.line < lineStart || lineEnd < loc.line) {
|
||||
throw new RangeError(
|
||||
`Line number out of range (line ${loc.line} requested). Valid range: ${lineStart}-${lineEnd}`,
|
||||
);
|
||||
}
|
||||
|
||||
// If the loc is at the start, return the start index of the root node.
|
||||
if (loc.line === lineStart && loc.column === columnStart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the loc is at the end, return the index one "spot" past the last character of the file.
|
||||
if (loc.line === lineEnd && loc.column === columnEnd) {
|
||||
return this.text.length;
|
||||
}
|
||||
|
||||
// Ensure `#lineStartIndices` are lazily calculated.
|
||||
this.#ensureLineStartIndicesFromLoc(loc, lineStart);
|
||||
|
||||
const isLastLine = loc.line === lineEnd;
|
||||
const lineStartIndex = this.#lineStartIndices[loc.line - lineStart];
|
||||
const lineEndIndex = isLastLine
|
||||
? this.text.length
|
||||
: this.#lineStartIndices[loc.line - lineStart + 1];
|
||||
const positionIndex = lineStartIndex + loc.column - columnStart;
|
||||
|
||||
if (
|
||||
loc.column < columnStart ||
|
||||
(isLastLine && positionIndex > lineEndIndex) ||
|
||||
(!isLastLine && positionIndex >= lineEndIndex)
|
||||
) {
|
||||
throw new RangeError(
|
||||
`Column number out of range (column ${loc.column} requested). Valid range for line ${loc.line}: ${columnStart}-${lineEndIndex - lineStartIndex + columnStart + (isLastLine ? 0 : -1)}`,
|
||||
);
|
||||
}
|
||||
|
||||
return positionIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the range information for the given node or token.
|
||||
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the range information for.
|
||||
* @returns {SourceRange} The range information for the node or token.
|
||||
* @throws {Error} If the node or token does not have range information.
|
||||
*/
|
||||
getRange(nodeOrToken) {
|
||||
if (hasESTreeStyleRange(nodeOrToken)) {
|
||||
return nodeOrToken.range;
|
||||
}
|
||||
|
||||
if (hasPosStyleRange(nodeOrToken)) {
|
||||
return [
|
||||
nodeOrToken.position.start.offset,
|
||||
nodeOrToken.position.end.offset,
|
||||
];
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Custom getRange() method must be implemented in the subclass.",
|
||||
);
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars -- Required to complete interface. */
|
||||
/**
|
||||
* Returns the parent of the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node to get the parent of.
|
||||
* @returns {Options['SyntaxElementWithLoc']|undefined} The parent of the node.
|
||||
* @throws {Error} If the method is not implemented in the subclass.
|
||||
*/
|
||||
getParent(node) {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
/* eslint-enable no-unused-vars -- Required to complete interface. */
|
||||
|
||||
/**
|
||||
* Gets all the ancestors of a given node
|
||||
* @param {Options['SyntaxElementWithLoc']} node The node
|
||||
* @returns {Array<Options['SyntaxElementWithLoc']>} All the ancestor nodes in the AST, not including the provided node, starting
|
||||
* from the root node at index 0 and going inwards to the parent node.
|
||||
* @throws {TypeError} When `node` is missing.
|
||||
*/
|
||||
getAncestors(node) {
|
||||
if (!node) {
|
||||
throw new TypeError("Missing required argument: node.");
|
||||
}
|
||||
|
||||
const ancestorsStartingAtParent = [];
|
||||
|
||||
for (
|
||||
let ancestor = this.getParent(node);
|
||||
ancestor;
|
||||
ancestor = this.getParent(ancestor)
|
||||
) {
|
||||
ancestorsStartingAtParent.push(ancestor);
|
||||
}
|
||||
|
||||
return ancestorsStartingAtParent.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source code for the given node.
|
||||
* @param {Options['SyntaxElementWithLoc']} [node] The AST node to get the text for.
|
||||
* @param {number} [beforeCount] The number of characters before the node to retrieve.
|
||||
* @param {number} [afterCount] The number of characters after the node to retrieve.
|
||||
* @returns {string} The text representing the AST node.
|
||||
* @public
|
||||
*/
|
||||
getText(node, beforeCount, afterCount) {
|
||||
if (node) {
|
||||
const range = this.getRange(node);
|
||||
return this.text.slice(
|
||||
Math.max(range[0] - (beforeCount || 0), 0),
|
||||
range[1] + (afterCount || 0),
|
||||
);
|
||||
}
|
||||
return this.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entire source text split into an array of lines.
|
||||
* @returns {Array<string>} The source text as an array of lines.
|
||||
* @public
|
||||
*/
|
||||
get lines() {
|
||||
this.#ensureLines(); // Ensure `#lines` is lazily calculated.
|
||||
|
||||
return this.#lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the source code and return the steps that were taken.
|
||||
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
|
||||
*/
|
||||
traverse() {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
export { CallMethodStep, ConfigCommentParser, Directive, TextSourceCodeBase, VisitNodeStep };
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @fileoverview Types for the plugin-kit package.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
import type { RuleDefinition, RuleDefinitionTypeOptions, RuleVisitor } from "@eslint/core";
|
||||
/**
|
||||
* Defaults for non-language-related `RuleDefinition` options.
|
||||
*/
|
||||
export interface CustomRuleTypeDefinitions {
|
||||
RuleOptions: unknown[];
|
||||
MessageIds: string;
|
||||
ExtRuleDocs: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* A helper type to define language specific specializations of the `RuleDefinition` type.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type YourRuleDefinition<
|
||||
* Options extends Partial<CustomRuleTypeDefinitions> = {},
|
||||
* > = CustomRuleDefinitionType<
|
||||
* {
|
||||
* LangOptions: YourLanguageOptions;
|
||||
* Code: YourSourceCode;
|
||||
* Visitor: YourRuleVisitor;
|
||||
* Node: YourNode;
|
||||
* },
|
||||
* Options
|
||||
* >;
|
||||
* ```
|
||||
*/
|
||||
export type CustomRuleDefinitionType<LanguageSpecificOptions extends Omit<RuleDefinitionTypeOptions, keyof CustomRuleTypeDefinitions>, Options extends Partial<CustomRuleTypeDefinitions>> = RuleDefinition<LanguageSpecificOptions & Required<Options & Omit<CustomRuleTypeDefinitions, keyof Options>>>;
|
||||
/**
|
||||
* Adds matching `:exit` selector properties for each key of a `RuleVisitor`.
|
||||
*/
|
||||
export type CustomRuleVisitorWithExit<RuleVisitorType extends RuleVisitor> = {
|
||||
[Key in keyof RuleVisitorType as Key | `${Key & string}:exit`]: RuleVisitorType[Key];
|
||||
};
|
||||
/**
|
||||
* A map of names to string values, or `null` when no value is provided.
|
||||
*/
|
||||
export type StringConfig = Record<string, string | null>;
|
||||
/**
|
||||
* A map of names to boolean flags.
|
||||
*/
|
||||
export type BooleanConfig = Record<string, boolean>;
|
||||
Generated
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @fileoverview Types for the plugin-kit package.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Imports
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
import type {
|
||||
RuleDefinition,
|
||||
RuleDefinitionTypeOptions,
|
||||
RuleVisitor,
|
||||
} from "@eslint/core";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Exports
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Defaults for non-language-related `RuleDefinition` options.
|
||||
*/
|
||||
export interface CustomRuleTypeDefinitions {
|
||||
RuleOptions: unknown[];
|
||||
MessageIds: string;
|
||||
ExtRuleDocs: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper type to define language specific specializations of the `RuleDefinition` type.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type YourRuleDefinition<
|
||||
* Options extends Partial<CustomRuleTypeDefinitions> = {},
|
||||
* > = CustomRuleDefinitionType<
|
||||
* {
|
||||
* LangOptions: YourLanguageOptions;
|
||||
* Code: YourSourceCode;
|
||||
* Visitor: YourRuleVisitor;
|
||||
* Node: YourNode;
|
||||
* },
|
||||
* Options
|
||||
* >;
|
||||
* ```
|
||||
*/
|
||||
export type CustomRuleDefinitionType<
|
||||
LanguageSpecificOptions extends Omit<
|
||||
RuleDefinitionTypeOptions,
|
||||
keyof CustomRuleTypeDefinitions
|
||||
>,
|
||||
Options extends Partial<CustomRuleTypeDefinitions>,
|
||||
> = RuleDefinition<
|
||||
// Language specific type options (non-configurable)
|
||||
LanguageSpecificOptions &
|
||||
Required<
|
||||
// Rule specific type options (custom)
|
||||
Options &
|
||||
// Rule specific type options (defaults)
|
||||
Omit<CustomRuleTypeDefinitions, keyof Options>
|
||||
>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Adds matching `:exit` selector properties for each key of a `RuleVisitor`.
|
||||
*/
|
||||
export type CustomRuleVisitorWithExit<RuleVisitorType extends RuleVisitor> = {
|
||||
[Key in keyof RuleVisitorType as
|
||||
| Key
|
||||
| `${Key & string}:exit`]: RuleVisitorType[Key];
|
||||
};
|
||||
|
||||
/**
|
||||
* A map of names to string values, or `null` when no value is provided.
|
||||
*/
|
||||
export type StringConfig = Record<string, string | null>;
|
||||
|
||||
/**
|
||||
* A map of names to boolean flags.
|
||||
*/
|
||||
export type BooleanConfig = Record<string, boolean>;
|
||||
Generated
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "@eslint/plugin-kit",
|
||||
"version": "0.6.1",
|
||||
"description": "Utilities for building ESLint plugins.",
|
||||
"author": "Nicholas C. Zakas",
|
||||
"type": "module",
|
||||
"main": "dist/esm/index.js",
|
||||
"types": "dist/esm/index.d.ts",
|
||||
"exports": {
|
||||
"require": {
|
||||
"types": "./dist/cjs/index.d.cts",
|
||||
"default": "./dist/cjs/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/eslint/rewrite.git",
|
||||
"directory": "packages/plugin-kit"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/eslint/rewrite/issues"
|
||||
},
|
||||
"homepage": "https://github.com/eslint/rewrite/tree/main/packages/plugin-kit#readme",
|
||||
"scripts": {
|
||||
"build:dedupe-types": "node ../../tools/dedupe-types.js dist/cjs/index.cjs dist/esm/index.js",
|
||||
"build:cts": "node ../../tools/build-cts.js dist/esm/index.d.ts dist/cjs/index.d.cts",
|
||||
"build": "rollup -c && npm run build:dedupe-types && tsc -p tsconfig.esm.json && npm run build:cts",
|
||||
"lint:types": "attw --pack",
|
||||
"pretest": "npm run build",
|
||||
"test": "npm run test:types && npm run test:unit",
|
||||
"test:coverage": "npm run build && c8 npm run test:unit",
|
||||
"test:jsr": "npx -y jsr@latest publish --dry-run",
|
||||
"test:types": "tsc -p tests/types/tsconfig.json",
|
||||
"test:unit": "mocha \"tests/**/*.test.js\""
|
||||
},
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"eslintplugin",
|
||||
"eslint-plugin"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^1.1.1",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/levn": "^0.4.0",
|
||||
"rollup-plugin-copy": "^3.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"name": "eslint-plugin-jsonc",
|
||||
"version": "3.1.2",
|
||||
"description": "ESLint plugin for JSON, JSONC and JSON5 files.",
|
||||
"type": "module",
|
||||
"main": "./dist/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "npm run -s clean",
|
||||
"build": "npm run build:ts",
|
||||
"build:ts": "tsdown",
|
||||
"clean": "node -e \"import('fs').then(fs=>['dist','coverage'].forEach(d=>{try{fs.rmSync(d,{recursive:true,force:true})}catch(e){}}))\"",
|
||||
"lint": "npm run eslint && npm run tsc",
|
||||
"tsc": "tsc --noEmit",
|
||||
"eslint": "eslint . --config=./eslint.config.mjs --no-config-lookup",
|
||||
"eslint-fix": "npm run eslint -- --fix",
|
||||
"test:base": "env-cmd -e test -- mocha --import=tsx/esm \"tests/lib/**/*.ts\" --reporter dot --timeout 60000",
|
||||
"test": "npm run test:base",
|
||||
"test:c8": "c8 --reporter=lcov npm run test:base",
|
||||
"pretest:integrations": "npm run build:ts && npm pack",
|
||||
"test:integrations": "mocha --import=tsx/esm \"tests-integrations/lib/**/*.ts\" --reporter dot --timeout 120000",
|
||||
"update": "node --import=tsx/esm ./tools/update.ts && npm run eslint-fix && npm run test:c8",
|
||||
"update-only": "node --import=tsx/esm ./tools/update.ts",
|
||||
"new": "node --import=tsx/esm ./tools/new-rule.ts",
|
||||
"predocs:watch": "npm run build:ts",
|
||||
"docs:watch": "vitepress dev docs",
|
||||
"docs:build": "npm run build:ts && vitepress build docs",
|
||||
"prerelease": "npm run test && npm run build",
|
||||
"release": "changeset publish",
|
||||
"preversion": "npm test && git add .",
|
||||
"version": "env-cmd -e version -- npm run update && git add .",
|
||||
"preversion:ci": "npm run build",
|
||||
"version:ci": "env-cmd -e version-ci -- npm run update && changeset version"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ota-meshi/eslint-plugin-jsonc.git"
|
||||
},
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"eslintplugin",
|
||||
"eslint-plugin",
|
||||
"lint",
|
||||
"jsonc",
|
||||
"json5",
|
||||
"json"
|
||||
],
|
||||
"author": "Yosuke Ota",
|
||||
"funding": "https://github.com/sponsors/ota-meshi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ota-meshi/eslint-plugin-jsonc/issues"
|
||||
},
|
||||
"homepage": "https://ota-meshi.github.io/eslint-plugin-jsonc/",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.5.1",
|
||||
"@eslint/core": "^1.0.1",
|
||||
"@eslint/plugin-kit": "^0.6.0",
|
||||
"@ota-meshi/ast-token-store": "^0.3.0",
|
||||
"diff-sequences": "^29.6.3",
|
||||
"eslint-json-compat-utils": "^0.2.3",
|
||||
"jsonc-eslint-parser": "^3.1.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"synckit": "^0.11.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.38.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.27.0",
|
||||
"@changesets/changelog-github": "^0.6.0",
|
||||
"@changesets/cli": "^2.28.1",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
|
||||
"@eslint/json": "^1.0.0",
|
||||
"@ota-meshi/eslint-plugin": "^0.20.0",
|
||||
"@ota-meshi/site-kit-eslint-editor-vue": "^0.2.4",
|
||||
"@stylistic/eslint-plugin": "^5.7.1",
|
||||
"@swc-node/register": "^1.11.1",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/natural-compare": "^1.4.3",
|
||||
"@types/node": "^24.0.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
||||
"@typescript-eslint/parser": "^8.28.0",
|
||||
"c8": "^11.0.0",
|
||||
"env-cmd": "^11.0.0",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-eslint-plugin": "^7.0.0",
|
||||
"eslint-plugin-eslint-rule-tester": "^0.6.0",
|
||||
"eslint-plugin-jsdoc": "^62.0.0",
|
||||
"eslint-plugin-json-schema-validator": "^6.0.0",
|
||||
"eslint-plugin-jsonc": "^3.0.0",
|
||||
"eslint-plugin-markdown": "^5.1.0",
|
||||
"eslint-plugin-n": "^17.23.2",
|
||||
"eslint-plugin-node-dependencies": "^2.0.0",
|
||||
"eslint-plugin-prettier": "^5.2.4",
|
||||
"eslint-plugin-regexp": "^3.0.0",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"eslint-plugin-yml": "^3.0.0",
|
||||
"eslint-scope": "^9.1.0",
|
||||
"events": "^3.3.0",
|
||||
"globals": "^17.0.0",
|
||||
"mocha": "^11.1.0",
|
||||
"pako": "^2.1.0",
|
||||
"prettier": "^3.5.3",
|
||||
"semver": "^7.7.1",
|
||||
"stylelint": "^17.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.6.0",
|
||||
"stylelint-config-standard": "^40.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-stylus": "^1.0.0",
|
||||
"tsdown": "^0.20.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"vite-plugin-eslint4b": "^0.7.0",
|
||||
"vitepress": "^1.6.3",
|
||||
"vue-eslint-parser": "^10.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user