FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

21
node_modules/eslint-plugin-solid/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Josh Wilson
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.

223
node_modules/eslint-plugin-solid/README.md generated vendored Normal file
View File

@@ -0,0 +1,223 @@
<p>
<img width="100%" src="https://assets.solidjs.com/banner?type=ESLint%20Plugin&background=tiles&project=%20" alt="Solid ESLint Extension">
</p>
# Solid ESLint Plugin
[![npm version](https://img.shields.io/npm/v/eslint-plugin-solid?style=for-the-badge)](https://npmjs.com/package/eslint-plugin-solid)
[![GitHub package version](https://img.shields.io/github/package-json/v/solidjs-community/eslint-plugin-solid?style=for-the-badge)](https://github.com/solidjs-community/eslint-plugin-solid)
![ESLint peer dependency](https://img.shields.io/badge/eslint-6.x--8.x-blue?style=for-the-badge)
[![CI](https://github.com/solidjs-community/eslint-plugin-solid/actions/workflows/ci.yml/badge.svg?style=for-the-badge)](https://github.com/solidjs-community/eslint-plugin-solid/actions/workflows/ci.yml)
This package contains [Solid](https://www.solidjs.com/)-specific linting rules for ESLint. It can
ease Solid's learning curve by finding and fixing problems around Solid's reactivity system, and can
migrate some React patterns to Solid code.
It's approaching a `1.0.0` release, and it's well tested and should be helpful in Solid projects
today.
<!-- AUTO-GENERATED-CONTENT:START (TOC) -->
- [Installation](#installation)
- [Configuration](#configuration)
- [TypeScript](#typescript)
- [Manual Configuration](#manual-configuration)
- [Flat Configuration](#flat-configuration)
- [Rules](#rules)
- [Troubleshooting](#troubleshooting)
- [Versioning](#versioning)
<!-- AUTO-GENERATED-CONTENT:END -->
## Installation
Install `eslint` and `eslint-plugin-solid` locally.
```sh
npm install --save-dev eslint eslint-plugin-solid
# or
pnpm add --save-dev eslint eslint-plugin-solid
yarn add --dev eslint eslint-plugin-solid
# optional, to create an ESLint config file
npx eslint --init
# or
pnpm eslint --init
yarn eslint --init
```
If you're using VSCode, you'll want to install the [ESLint
extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). You're
encouraged to enable auto-fixing problems on save by adding the following to your `settings.json`
file.
```json
{
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
```
If you're using Vite, you may want to install
[vite-plugin-eslint](https://github.com/gxmari007/vite-plugin-eslint).
You may also want to check out
[eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y), which provides
useful rules for writing accessible HTML.
## Configuration
Use the `"plugin:solid/recommended"` configuration to get reasonable defaults as shown [below](#rules).
```json
{
"plugins": ["solid"],
"extends": ["eslint:recommended", "plugin:solid/recommended"]
}
```
### TypeScript
If you're using TypeScript, use the `"plugin:solid/typescript"` configuration instead.
This disables some features that overlap with type checking.
```json
{
"parser": "@typescript-eslint/parser",
"plugins": ["solid"],
"extends": ["eslint:recommended", "plugin:solid/typescript"]
}
```
### Manual Configuration
If you don't want to use a preset, you can configure rules individually. Add the `"solid"` plugin,
enable JSX with the parser options (or use the equivalent options for `@typescript-eslint/parser` or
`@babel/eslint-parser`), and configure the rules you would like to use. Some rules have additional
options you can set.
```json
{
"plugins": ["solid"],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"solid/reactivity": "warn",
"solid/no-destructure": "warn",
"solid/jsx-no-undef": "error"
}
}
```
### Flat Configuration
ESLint's new configuration system, [Flat
Configuration](https://eslint.org/docs/latest/use/configure/configuration-files-new#using-configurations-included-in-plugins),
is available starting in ESLint [v8.23.0](https://github.com/eslint/eslint/releases/tag/v8.23.0). To
use it, create an `eslint.config.js` file at the root of your project, instead of `.eslintrc.*`
and/or `.eslintignore`.
```js
import js from "@eslint/js";
import solid from "eslint-plugin-solid/configs/recommended";
export default [
js.configs.recommended, // replaces eslint:recommended
solid,
];
```
For TypeScript:
```js
import js from "@eslint/js";
import solid from 'eslint-plugin-solid/configs/typescript';
import * as tsParser from "@typescript-eslint/parser";
export default [
js.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
...solid,
languageOptions: {
parser: tsParser,
parserOptions: {
project: 'tsconfig.json',
},
},
},
]
```
These configurations do not configure global variables in ESLint. You can do this yourself manually
or with a package like [globals](https://www.npmjs.com/package/globals) by creating a configuration
with a `languageOptions.globals` object. We recommend setting up global variables for Browser APIs
as well as at least ES2015.
Note for the ESLint VSCode Extension: Enable the "Use Flat Config" setting for your workspace to
enable Flat Config support.
## Rules
✔: Enabled in the `recommended` configuration.
🔧: Fixable with [`eslint --fix`](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems)/IDE auto-fix.
<!-- AUTO-GENERATED-CONTENT:START (RULES) -->
| ✔ | 🔧 | Rule | Description |
| :---: | :---: | :--- | :--- |
| ✔ | 🔧 | [solid/components-return-once](docs/components-return-once.md) | Disallow early returns in components. Solid components only run once, and so conditionals should be inside JSX. |
| ✔ | 🔧 | [solid/event-handlers](docs/event-handlers.md) | Enforce naming DOM element event handlers consistently and prevent Solid's analysis from misunderstanding whether a prop should be an event handler. |
| ✔ | 🔧 | [solid/imports](docs/imports.md) | Enforce consistent imports from "solid-js", "solid-js/web", and "solid-js/store". |
| ✔ | | [solid/jsx-no-duplicate-props](docs/jsx-no-duplicate-props.md) | Disallow passing the same prop twice in JSX. |
| ✔ | | [solid/jsx-no-script-url](docs/jsx-no-script-url.md) | Disallow javascript: URLs. |
| ✔ | 🔧 | [solid/jsx-no-undef](docs/jsx-no-undef.md) | Disallow references to undefined variables in JSX. Handles custom directives. |
| ✔ | | [solid/jsx-uses-vars](docs/jsx-uses-vars.md) | Prevent variables used in JSX from being marked as unused. |
| | | [solid/no-array-handlers](docs/no-array-handlers.md) | Disallow usage of type-unsafe event handlers. |
| ✔ | 🔧 | [solid/no-destructure](docs/no-destructure.md) | Disallow destructuring props. In Solid, props must be used with property accesses (`props.foo`) to preserve reactivity. This rule only tracks destructuring in the parameter list. |
| ✔ | 🔧 | [solid/no-innerhtml](docs/no-innerhtml.md) | Disallow usage of the innerHTML attribute, which can often lead to security vulnerabilities. |
| | | [solid/no-proxy-apis](docs/no-proxy-apis.md) | Disallow usage of APIs that use ES6 Proxies, only to target environments that don't support them. |
| ✔ | 🔧 | [solid/no-react-deps](docs/no-react-deps.md) | Disallow usage of dependency arrays in `createEffect` and `createMemo`. |
| ✔ | 🔧 | [solid/no-react-specific-props](docs/no-react-specific-props.md) | Disallow usage of React-specific `className`/`htmlFor` props, which were deprecated in v1.4.0. |
| ✔ | | [solid/no-unknown-namespaces](docs/no-unknown-namespaces.md) | Enforce using only Solid-specific namespaced attribute names (i.e. `'on:'` in `<div on:click={...} />`). |
| | 🔧 | [solid/prefer-classlist](docs/prefer-classlist.md) | Enforce using the classlist prop over importing a classnames helper. The classlist prop accepts an object `{ [class: string]: boolean }` just like classnames. |
| ✔ | 🔧 | [solid/prefer-for](docs/prefer-for.md) | Enforce using Solid's `<For />` component for mapping an array to JSX elements. |
| | 🔧 | [solid/prefer-show](docs/prefer-show.md) | Enforce using Solid's `<Show />` component for conditionally showing content. Solid's compiler covers this case, so it's a stylistic rule only. |
| ✔ | | [solid/reactivity](docs/reactivity.md) | Enforce that reactivity (props, signals, memos, etc.) is properly used, so changes in those values will be tracked and update the view as expected. |
| ✔ | 🔧 | [solid/self-closing-comp](docs/self-closing-comp.md) | Disallow extra closing tags for components without children. |
| ✔ | 🔧 | [solid/style-prop](docs/style-prop.md) | Require CSS properties in the `style` prop to be valid and kebab-cased (ex. 'font-size'), not camel-cased (ex. 'fontSize') like in React, and that property values with dimensions are strings, not numbers with implicit 'px' units. |
<!-- AUTO-GENERATED-CONTENT:END -->
## Troubleshooting
The rules in this plugin provide sensible guidelines as well as possible, but there may be times
where you know better than the rule and want to ignore a warning. To do that, [add a
comment](https://eslint.org/docs/latest/user-guide/configuring/rules#disabling-rules) like the
following:
```jsx
// eslint-disable-next-line solid/reactivity
const [editedValue, setEditedValue] = createSignal(props.value);
```
_Please note_: there may also be times where a rule correctly warns about a subtle problem,
even if it looks like a false positive at first. With `solid/reactivity`, please look at the
[reactivity docs](./docs/reactivity.md#troubleshooting) before deciding to disable the rule.
When in doubt, feel free to [file an
issue](https://github.com/solidjs-community/eslint-plugin-solid/issues/new/choose).
## Versioning
Pre-1.0.0, the rules and the `recommended` and `typescript` configuations will be
stable across patch (`0.0.x`) versions, but may change across minor (`0.x`) versions.
If you want to pin a minor version, use a tilde in your `package.json`.
<!-- AUTO-GENERATED-CONTENT:START (TILDE) -->
```diff
- "eslint-plugin-solid": "^0.13.2"
+ "eslint-plugin-solid": "~0.13.2"
```
<!-- AUTO-GENERATED-CONTENT:END -->

View File

@@ -0,0 +1,4 @@
declare module "eslint-plugin-solid/configs/recommended" {
const config: typeof import("../src/configs/recommended");
export = config;
}

View File

@@ -0,0 +1 @@
module.exports = require("../dist/configs/recommended");

View File

@@ -0,0 +1,4 @@
declare module "eslint-plugin-solid/configs/typescript" {
const config: typeof import("../src/configs/typescript");
export = config;
}

View File

@@ -0,0 +1 @@
module.exports = require("../dist/configs/typescript");

View File

@@ -0,0 +1,83 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const recommended: {
plugins: {
solid: {
rules: {
"components-return-once": TSESLint.RuleModule<"noEarlyReturn" | "noConditionalReturn", [], TSESLint.RuleListener>;
"event-handlers": TSESLint.RuleModule<"naming" | "capitalization" | "nonstandard" | "make-handler" | "make-attr" | "detected-attr" | "spread-handler", [({
ignoreCase?: boolean | undefined;
warnOnSpread?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
imports: TSESLint.RuleModule<"prefer-source", [], TSESLint.RuleListener>;
"jsx-no-duplicate-props": TSESLint.RuleModule<"noDuplicateProps" | "noDuplicateClass" | "noDuplicateChildren", [({
ignoreCase?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-undef": TSESLint.RuleModule<"undefined" | "customDirectiveUndefined" | "autoImport", [({
allowGlobals?: boolean | undefined;
autoImport?: boolean | undefined;
typescriptEnabled?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-script-url": TSESLint.RuleModule<"noJSURL", [], TSESLint.RuleListener>;
"jsx-uses-vars": TSESLint.RuleModule<never, [], TSESLint.RuleListener>;
"no-destructure": TSESLint.RuleModule<"noDestructure", [], TSESLint.RuleListener>;
"no-innerhtml": TSESLint.RuleModule<"dangerous" | "conflict" | "notHtml" | "useInnerText" | "dangerouslySetInnerHTML", [({
allowStatic?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-proxy-apis": TSESLint.RuleModule<"mergeProps" | "noStore" | "spreadCall" | "spreadMember" | "proxyLiteral", [], TSESLint.RuleListener>;
"no-react-deps": TSESLint.RuleModule<"noUselessDep", [], TSESLint.RuleListener>;
"no-react-specific-props": TSESLint.RuleModule<"prefer" | "noUselessKey", [], TSESLint.RuleListener>;
"no-unknown-namespaces": TSESLint.RuleModule<"style" | "unknown" | "component" | "component-suggest", [({
allowedNamespaces: string[];
} | undefined)?], TSESLint.RuleListener>;
"prefer-classlist": TSESLint.RuleModule<"preferClasslist", [({
classnames?: string[] | undefined;
} | undefined)?], TSESLint.RuleListener>;
"prefer-for": TSESLint.RuleModule<"preferFor" | "preferForOrIndex", [], TSESLint.RuleListener>;
"prefer-show": TSESLint.RuleModule<"preferShowAnd" | "preferShowTernary", [], TSESLint.RuleListener>;
reactivity: TSESLint.RuleModule<"noWrite" | "untrackedReactive" | "expectedFunctionGotExpression" | "badSignal" | "badUnnamedDerivedSignal" | "shouldDestructure" | "shouldAssign" | "noAsyncTrackedScope", [{
customReactiveFunctions: string[];
}], TSESLint.RuleListener>;
"self-closing-comp": TSESLint.RuleModule<"selfClose" | "dontSelfClose", [({
component?: "all" | "none" | undefined;
html?: "void" | "all" | "none" | undefined;
} | undefined)?], TSESLint.RuleListener>;
"style-prop": TSESLint.RuleModule<"kebabStyleProp" | "invalidStyleProp" | "numericStyleValue" | "stringStyle", [({
styleProps?: string[] | undefined;
allowString?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-array-handlers": TSESLint.RuleModule<"noArrayHandlers", [], TSESLint.RuleListener>;
};
};
};
languageOptions: {
sourceType: string;
parserOptions: {
ecmaFeatures: {
jsx: boolean;
};
};
};
rules: {
"solid/jsx-no-duplicate-props": number;
"solid/jsx-no-undef": number;
"solid/jsx-uses-vars": number;
"solid/no-unknown-namespaces": number;
"solid/no-innerhtml": number;
"solid/jsx-no-script-url": number;
"solid/components-return-once": number;
"solid/no-destructure": number;
"solid/prefer-for": number;
"solid/reactivity": number;
"solid/event-handlers": number;
"solid/imports": number;
"solid/style-prop": number;
"solid/no-react-deps": number;
"solid/no-react-specific-props": number;
"solid/self-closing-comp": number;
"solid/no-array-handlers": number;
"solid/prefer-show": number;
"solid/no-proxy-apis": number;
"solid/prefer-classlist": number;
};
};
export = recommended;

View File

@@ -0,0 +1,38 @@
"use strict";
const plugin_1 = require("../plugin");
const recommended = {
plugins: {
solid: plugin_1.plugin,
},
languageOptions: {
sourceType: "module",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
"solid/jsx-no-duplicate-props": 2,
"solid/jsx-no-undef": 2,
"solid/jsx-uses-vars": 2,
"solid/no-unknown-namespaces": 2,
"solid/no-innerhtml": 2,
"solid/jsx-no-script-url": 2,
"solid/components-return-once": 1,
"solid/no-destructure": 2,
"solid/prefer-for": 2,
"solid/reactivity": 1,
"solid/event-handlers": 1,
"solid/imports": 1,
"solid/style-prop": 1,
"solid/no-react-deps": 1,
"solid/no-react-specific-props": 1,
"solid/self-closing-comp": 1,
"solid/no-array-handlers": 0,
"solid/prefer-show": 0,
"solid/no-proxy-apis": 0,
"solid/prefer-classlist": 0,
},
};
module.exports = recommended;

View File

@@ -0,0 +1,77 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const typescript: {
plugins: {
solid: {
rules: {
"components-return-once": TSESLint.RuleModule<"noEarlyReturn" | "noConditionalReturn", [], TSESLint.RuleListener>;
"event-handlers": TSESLint.RuleModule<"naming" | "capitalization" | "nonstandard" | "make-handler" | "make-attr" | "detected-attr" | "spread-handler", [({
ignoreCase?: boolean | undefined;
warnOnSpread?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
imports: TSESLint.RuleModule<"prefer-source", [], TSESLint.RuleListener>;
"jsx-no-duplicate-props": TSESLint.RuleModule<"noDuplicateProps" | "noDuplicateClass" | "noDuplicateChildren", [({
ignoreCase?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-undef": TSESLint.RuleModule<"undefined" | "customDirectiveUndefined" | "autoImport", [({
allowGlobals?: boolean | undefined;
autoImport?: boolean | undefined;
typescriptEnabled?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-script-url": TSESLint.RuleModule<"noJSURL", [], TSESLint.RuleListener>;
"jsx-uses-vars": TSESLint.RuleModule<never, [], TSESLint.RuleListener>;
"no-destructure": TSESLint.RuleModule<"noDestructure", [], TSESLint.RuleListener>;
"no-innerhtml": TSESLint.RuleModule<"dangerous" | "conflict" | "notHtml" | "useInnerText" | "dangerouslySetInnerHTML", [({
allowStatic?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-proxy-apis": TSESLint.RuleModule<"mergeProps" | "noStore" | "spreadCall" | "spreadMember" | "proxyLiteral", [], TSESLint.RuleListener>;
"no-react-deps": TSESLint.RuleModule<"noUselessDep", [], TSESLint.RuleListener>;
"no-react-specific-props": TSESLint.RuleModule<"prefer" | "noUselessKey", [], TSESLint.RuleListener>;
"no-unknown-namespaces": TSESLint.RuleModule<"style" | "unknown" | "component" | "component-suggest", [({
allowedNamespaces: string[];
} | undefined)?], TSESLint.RuleListener>;
"prefer-classlist": TSESLint.RuleModule<"preferClasslist", [({
classnames?: string[] | undefined;
} | undefined)?], TSESLint.RuleListener>;
"prefer-for": TSESLint.RuleModule<"preferFor" | "preferForOrIndex", [], TSESLint.RuleListener>;
"prefer-show": TSESLint.RuleModule<"preferShowAnd" | "preferShowTernary", [], TSESLint.RuleListener>;
reactivity: TSESLint.RuleModule<"noWrite" | "untrackedReactive" | "expectedFunctionGotExpression" | "badSignal" | "badUnnamedDerivedSignal" | "shouldDestructure" | "shouldAssign" | "noAsyncTrackedScope", [{
customReactiveFunctions: string[];
}], TSESLint.RuleListener>;
"self-closing-comp": TSESLint.RuleModule<"selfClose" | "dontSelfClose", [({
component?: "all" | "none" | undefined;
html?: "void" | "all" | "none" | undefined;
} | undefined)?], TSESLint.RuleListener>;
"style-prop": TSESLint.RuleModule<"kebabStyleProp" | "invalidStyleProp" | "numericStyleValue" | "stringStyle", [({
styleProps?: string[] | undefined;
allowString?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-array-handlers": TSESLint.RuleModule<"noArrayHandlers", [], TSESLint.RuleListener>;
};
};
};
rules: {
"solid/jsx-no-undef": (number | {
typescriptEnabled: boolean;
})[];
"solid/no-unknown-namespaces": number;
"solid/jsx-no-duplicate-props": number;
"solid/jsx-uses-vars": number;
"solid/no-innerhtml": number;
"solid/jsx-no-script-url": number;
"solid/components-return-once": number;
"solid/no-destructure": number;
"solid/prefer-for": number;
"solid/reactivity": number;
"solid/event-handlers": number;
"solid/imports": number;
"solid/style-prop": number;
"solid/no-react-deps": number;
"solid/no-react-specific-props": number;
"solid/self-closing-comp": number;
"solid/no-array-handlers": number;
"solid/prefer-show": number;
"solid/no-proxy-apis": number;
"solid/prefer-classlist": number;
};
};
export = typescript;

View File

@@ -0,0 +1,14 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const recommended_1 = __importDefault(require("./recommended"));
const typescript = {
plugins: recommended_1.default.plugins,
rules: {
...recommended_1.default.rules,
"solid/jsx-no-undef": [2, { typescriptEnabled: true }],
"solid/no-unknown-namespaces": 0,
},
};
module.exports = typescript;

119
node_modules/eslint-plugin-solid/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,119 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const pluginLegacy: {
rules: {
"components-return-once": TSESLint.RuleModule<"noEarlyReturn" | "noConditionalReturn", [], TSESLint.RuleListener>;
"event-handlers": TSESLint.RuleModule<"naming" | "capitalization" | "nonstandard" | "make-handler" | "make-attr" | "detected-attr" | "spread-handler", [({
ignoreCase?: boolean | undefined;
warnOnSpread?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
imports: TSESLint.RuleModule<"prefer-source", [], TSESLint.RuleListener>;
"jsx-no-duplicate-props": TSESLint.RuleModule<"noDuplicateProps" | "noDuplicateClass" | "noDuplicateChildren", [({
ignoreCase?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-undef": TSESLint.RuleModule<"undefined" | "customDirectiveUndefined" | "autoImport", [({
allowGlobals?: boolean | undefined;
autoImport?: boolean | undefined;
typescriptEnabled?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-script-url": TSESLint.RuleModule<"noJSURL", [], TSESLint.RuleListener>;
"jsx-uses-vars": TSESLint.RuleModule<never, [], TSESLint.RuleListener>;
"no-destructure": TSESLint.RuleModule<"noDestructure", [], TSESLint.RuleListener>;
"no-innerhtml": TSESLint.RuleModule<"dangerous" | "conflict" | "notHtml" | "useInnerText" | "dangerouslySetInnerHTML", [({
allowStatic?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-proxy-apis": TSESLint.RuleModule<"mergeProps" | "noStore" | "spreadCall" | "spreadMember" | "proxyLiteral", [], TSESLint.RuleListener>;
"no-react-deps": TSESLint.RuleModule<"noUselessDep", [], TSESLint.RuleListener>;
"no-react-specific-props": TSESLint.RuleModule<"prefer" | "noUselessKey", [], TSESLint.RuleListener>;
"no-unknown-namespaces": TSESLint.RuleModule<"style" | "unknown" | "component" | "component-suggest", [({
allowedNamespaces: string[];
} | undefined)?], TSESLint.RuleListener>;
"prefer-classlist": TSESLint.RuleModule<"preferClasslist", [({
classnames?: string[] | undefined;
} | undefined)?], TSESLint.RuleListener>;
"prefer-for": TSESLint.RuleModule<"preferFor" | "preferForOrIndex", [], TSESLint.RuleListener>;
"prefer-show": TSESLint.RuleModule<"preferShowAnd" | "preferShowTernary", [], TSESLint.RuleListener>;
reactivity: TSESLint.RuleModule<"noWrite" | "untrackedReactive" | "expectedFunctionGotExpression" | "badSignal" | "badUnnamedDerivedSignal" | "shouldDestructure" | "shouldAssign" | "noAsyncTrackedScope", [{
customReactiveFunctions: string[];
}], TSESLint.RuleListener>;
"self-closing-comp": TSESLint.RuleModule<"selfClose" | "dontSelfClose", [({
component?: "all" | "none" | undefined;
html?: "void" | "all" | "none" | undefined;
} | undefined)?], TSESLint.RuleListener>;
"style-prop": TSESLint.RuleModule<"kebabStyleProp" | "invalidStyleProp" | "numericStyleValue" | "stringStyle", [({
styleProps?: string[] | undefined;
allowString?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-array-handlers": TSESLint.RuleModule<"noArrayHandlers", [], TSESLint.RuleListener>;
};
configs: {
recommended: {
plugins: string[];
env: {
browser: boolean;
es6: boolean;
};
parserOptions: {
ecmaFeatures: {
jsx: boolean;
};
};
rules: {
"solid/jsx-no-duplicate-props": number;
"solid/jsx-no-undef": number;
"solid/jsx-uses-vars": number;
"solid/no-unknown-namespaces": number;
"solid/no-innerhtml": number;
"solid/jsx-no-script-url": number;
"solid/components-return-once": number;
"solid/no-destructure": number;
"solid/prefer-for": number;
"solid/reactivity": number;
"solid/event-handlers": number;
"solid/imports": number;
"solid/style-prop": number;
"solid/no-react-deps": number;
"solid/no-react-specific-props": number;
"solid/self-closing-comp": number;
"solid/no-array-handlers": number;
"solid/prefer-show": number;
"solid/no-proxy-apis": number;
"solid/prefer-classlist": number;
};
};
typescript: {
plugins: string[];
env: {
browser: boolean;
es6: boolean;
};
parserOptions: {
sourceType: string;
};
rules: {
"solid/jsx-no-undef": (number | {
typescriptEnabled: boolean;
})[];
"solid/no-unknown-namespaces": number;
"solid/jsx-no-duplicate-props": number;
"solid/jsx-uses-vars": number;
"solid/no-innerhtml": number;
"solid/jsx-no-script-url": number;
"solid/components-return-once": number;
"solid/no-destructure": number;
"solid/prefer-for": number;
"solid/reactivity": number;
"solid/event-handlers": number;
"solid/imports": number;
"solid/style-prop": number;
"solid/no-react-deps": number;
"solid/no-react-specific-props": number;
"solid/self-closing-comp": number;
"solid/no-array-handlers": number;
"solid/prefer-show": number;
"solid/no-proxy-apis": number;
"solid/prefer-classlist": number;
};
};
};
};
export = pluginLegacy;

33
node_modules/eslint-plugin-solid/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const plugin_1 = require("./plugin");
const recommended_1 = __importDefault(require("./configs/recommended"));
const typescript_1 = __importDefault(require("./configs/typescript"));
const pluginLegacy = {
rules: plugin_1.plugin.rules,
configs: {
recommended: {
plugins: ["solid"],
env: {
browser: true,
es6: true,
},
parserOptions: recommended_1.default.languageOptions.parserOptions,
rules: recommended_1.default.rules,
},
typescript: {
plugins: ["solid"],
env: {
browser: true,
es6: true,
},
parserOptions: {
sourceType: "module",
},
rules: typescript_1.default.rules,
},
},
};
module.exports = pluginLegacy;

48
node_modules/eslint-plugin-solid/dist/plugin.d.ts generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import type { TSESLint } from "@typescript-eslint/utils";
export declare const plugin: {
rules: {
"components-return-once": TSESLint.RuleModule<"noEarlyReturn" | "noConditionalReturn", [], TSESLint.RuleListener>;
"event-handlers": TSESLint.RuleModule<"naming" | "capitalization" | "nonstandard" | "make-handler" | "make-attr" | "detected-attr" | "spread-handler", [({
ignoreCase?: boolean | undefined;
warnOnSpread?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
imports: TSESLint.RuleModule<"prefer-source", [], TSESLint.RuleListener>;
"jsx-no-duplicate-props": TSESLint.RuleModule<"noDuplicateProps" | "noDuplicateClass" | "noDuplicateChildren", [({
ignoreCase?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-undef": TSESLint.RuleModule<"undefined" | "customDirectiveUndefined" | "autoImport", [({
allowGlobals?: boolean | undefined;
autoImport?: boolean | undefined;
typescriptEnabled?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"jsx-no-script-url": TSESLint.RuleModule<"noJSURL", [], TSESLint.RuleListener>;
"jsx-uses-vars": TSESLint.RuleModule<never, [], TSESLint.RuleListener>;
"no-destructure": TSESLint.RuleModule<"noDestructure", [], TSESLint.RuleListener>;
"no-innerhtml": TSESLint.RuleModule<"dangerous" | "conflict" | "notHtml" | "useInnerText" | "dangerouslySetInnerHTML", [({
allowStatic?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-proxy-apis": TSESLint.RuleModule<"mergeProps" | "noStore" | "spreadCall" | "spreadMember" | "proxyLiteral", [], TSESLint.RuleListener>;
"no-react-deps": TSESLint.RuleModule<"noUselessDep", [], TSESLint.RuleListener>;
"no-react-specific-props": TSESLint.RuleModule<"prefer" | "noUselessKey", [], TSESLint.RuleListener>;
"no-unknown-namespaces": TSESLint.RuleModule<"style" | "unknown" | "component" | "component-suggest", [({
allowedNamespaces: string[];
} | undefined)?], TSESLint.RuleListener>;
"prefer-classlist": TSESLint.RuleModule<"preferClasslist", [({
classnames?: string[] | undefined;
} | undefined)?], TSESLint.RuleListener>;
"prefer-for": TSESLint.RuleModule<"preferFor" | "preferForOrIndex", [], TSESLint.RuleListener>;
"prefer-show": TSESLint.RuleModule<"preferShowAnd" | "preferShowTernary", [], TSESLint.RuleListener>;
reactivity: TSESLint.RuleModule<"noWrite" | "untrackedReactive" | "expectedFunctionGotExpression" | "badSignal" | "badUnnamedDerivedSignal" | "shouldDestructure" | "shouldAssign" | "noAsyncTrackedScope", [{
customReactiveFunctions: string[];
}], TSESLint.RuleListener>;
"self-closing-comp": TSESLint.RuleModule<"selfClose" | "dontSelfClose", [({
component?: "all" | "none" | undefined;
html?: "void" | "all" | "none" | undefined;
} | undefined)?], TSESLint.RuleListener>;
"style-prop": TSESLint.RuleModule<"kebabStyleProp" | "invalidStyleProp" | "numericStyleValue" | "stringStyle", [({
styleProps?: string[] | undefined;
allowString?: boolean | undefined;
} | undefined)?], TSESLint.RuleListener>;
"no-array-handlers": TSESLint.RuleModule<"noArrayHandlers", [], TSESLint.RuleListener>;
};
};

49
node_modules/eslint-plugin-solid/dist/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugin = void 0;
const components_return_once_1 = __importDefault(require("./rules/components-return-once"));
const event_handlers_1 = __importDefault(require("./rules/event-handlers"));
const imports_1 = __importDefault(require("./rules/imports"));
const jsx_no_duplicate_props_1 = __importDefault(require("./rules/jsx-no-duplicate-props"));
const jsx_no_script_url_1 = __importDefault(require("./rules/jsx-no-script-url"));
const jsx_no_undef_1 = __importDefault(require("./rules/jsx-no-undef"));
const jsx_uses_vars_1 = __importDefault(require("./rules/jsx-uses-vars"));
const no_destructure_1 = __importDefault(require("./rules/no-destructure"));
const no_innerhtml_1 = __importDefault(require("./rules/no-innerhtml"));
const no_proxy_apis_1 = __importDefault(require("./rules/no-proxy-apis"));
const no_react_deps_1 = __importDefault(require("./rules/no-react-deps"));
const no_react_specific_props_1 = __importDefault(require("./rules/no-react-specific-props"));
const no_unknown_namespaces_1 = __importDefault(require("./rules/no-unknown-namespaces"));
const prefer_classlist_1 = __importDefault(require("./rules/prefer-classlist"));
const prefer_for_1 = __importDefault(require("./rules/prefer-for"));
const prefer_show_1 = __importDefault(require("./rules/prefer-show"));
const reactivity_1 = __importDefault(require("./rules/reactivity"));
const self_closing_comp_1 = __importDefault(require("./rules/self-closing-comp"));
const style_prop_1 = __importDefault(require("./rules/style-prop"));
const no_array_handlers_1 = __importDefault(require("./rules/no-array-handlers"));
const allRules = {
"components-return-once": components_return_once_1.default,
"event-handlers": event_handlers_1.default,
imports: imports_1.default,
"jsx-no-duplicate-props": jsx_no_duplicate_props_1.default,
"jsx-no-undef": jsx_no_undef_1.default,
"jsx-no-script-url": jsx_no_script_url_1.default,
"jsx-uses-vars": jsx_uses_vars_1.default,
"no-destructure": no_destructure_1.default,
"no-innerhtml": no_innerhtml_1.default,
"no-proxy-apis": no_proxy_apis_1.default,
"no-react-deps": no_react_deps_1.default,
"no-react-specific-props": no_react_specific_props_1.default,
"no-unknown-namespaces": no_unknown_namespaces_1.default,
"prefer-classlist": prefer_classlist_1.default,
"prefer-for": prefer_for_1.default,
"prefer-show": prefer_show_1.default,
reactivity: reactivity_1.default,
"self-closing-comp": self_closing_comp_1.default,
"style-prop": style_prop_1.default,
"no-array-handlers": no_array_handlers_1.default,
};
exports.plugin = { rules: allRules };

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"noEarlyReturn" | "noConditionalReturn", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,149 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const isNothing = (node) => {
if (!node) {
return true;
}
switch (node.type) {
case "Literal":
return [null, undefined, false, ""].includes(node.value);
case "JSXFragment":
return !node.children || node.children.every(isNothing);
default:
return false;
}
};
const getLineLength = (loc) => loc.end.line - loc.start.line + 1;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow early returns in components. Solid components only run once, and so conditionals should be inside JSX.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/components-return-once.md",
},
fixable: "code",
schema: [],
messages: {
noEarlyReturn: "Solid components run once, so an early return breaks reactivity. Move the condition inside a JSX element, such as a fragment or <Show />.",
noConditionalReturn: "Solid components run once, so a conditional return breaks reactivity. Move the condition inside a JSX element, such as a fragment or <Show />.",
},
},
defaultOptions: [],
create(context) {
const functionStack = [];
const putIntoJSX = (node) => {
const text = context.getSourceCode().getText(node);
return node.type === "JSXElement" || node.type === "JSXFragment" ? text : `{${text}}`;
};
const currentFunction = () => functionStack[functionStack.length - 1];
const onFunctionEnter = (node) => {
let lastReturn;
if (node.body.type === "BlockStatement") {
const { length } = node.body.body;
const last = length && node.body.body[length - 1];
if (last && last.type === "ReturnStatement") {
lastReturn = last;
}
}
functionStack.push({ isComponent: false, lastReturn, earlyReturns: [] });
};
const onFunctionExit = (node) => {
if ((0, utils_2.getFunctionName)(node)?.match(/^[a-z]/) ||
node.parent?.type === "JSXExpressionContainer" ||
(node.parent?.type === "CallExpression" &&
node.parent.arguments.some((n) => n === node) &&
!node.parent.callee.name?.match(/^[A-Z]/))) {
currentFunction().isComponent = false;
}
if (currentFunction().isComponent) {
currentFunction().earlyReturns.forEach((earlyReturn) => {
context.report({
node: earlyReturn,
messageId: "noEarlyReturn",
});
});
const argument = currentFunction().lastReturn?.argument;
if (argument?.type === "ConditionalExpression") {
const sourceCode = context.getSourceCode();
context.report({
node: argument.parent,
messageId: "noConditionalReturn",
fix: (fixer) => {
const { test, consequent, alternate } = argument;
const conditions = [{ test, consequent }];
let fallback = alternate;
while (fallback.type === "ConditionalExpression") {
conditions.push({ test: fallback.test, consequent: fallback.consequent });
fallback = fallback.alternate;
}
if (conditions.length >= 2) {
const fallbackStr = !isNothing(fallback)
? ` fallback={${sourceCode.getText(fallback)}}`
: "";
return fixer.replaceText(argument, `<Switch${fallbackStr}>\n${conditions
.map(({ test, consequent }) => `<Match when={${sourceCode.getText(test)}}>${putIntoJSX(consequent)}</Match>`)
.join("\n")}\n</Switch>`);
}
if (isNothing(consequent)) {
return fixer.replaceText(argument, `<Show when={!(${sourceCode.getText(test)})}>${putIntoJSX(alternate)}</Show>`);
}
if (isNothing(fallback) ||
getLineLength(consequent.loc) >= getLineLength(alternate.loc) * 1.5) {
const fallbackStr = !isNothing(fallback)
? ` fallback={${sourceCode.getText(fallback)}}`
: "";
return fixer.replaceText(argument, `<Show when={${sourceCode.getText(test)}}${fallbackStr}>${putIntoJSX(consequent)}</Show>`);
}
return fixer.replaceText(argument, `<>${putIntoJSX(argument)}</>`);
},
});
}
else if (argument?.type === "LogicalExpression")
if (argument.operator === "&&") {
const sourceCode = context.getSourceCode();
context.report({
node: argument,
messageId: "noConditionalReturn",
fix: (fixer) => {
const { left: test, right: consequent } = argument;
return fixer.replaceText(argument, `<Show when={${sourceCode.getText(test)}}>${putIntoJSX(consequent)}</Show>`);
},
});
}
else {
context.report({
node: argument,
messageId: "noConditionalReturn",
});
}
}
functionStack.pop();
};
return {
FunctionDeclaration: onFunctionEnter,
FunctionExpression: onFunctionEnter,
ArrowFunctionExpression: onFunctionEnter,
"FunctionDeclaration:exit": onFunctionExit,
"FunctionExpression:exit": onFunctionExit,
"ArrowFunctionExpression:exit": onFunctionExit,
JSXElement() {
if (functionStack.length) {
currentFunction().isComponent = true;
}
},
JSXFragment() {
if (functionStack.length) {
currentFunction().isComponent = true;
}
},
ReturnStatement(node) {
if (functionStack.length && node !== currentFunction().lastReturn) {
currentFunction().earlyReturns.push(node);
}
},
};
},
});

View File

@@ -0,0 +1,8 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "naming" | "capitalization" | "nonstandard" | "make-handler" | "make-attr" | "detected-attr" | "spread-handler";
type Options = [{
ignoreCase?: boolean;
warnOnSpread?: boolean;
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,236 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getStaticValue } = utils_1.ASTUtils;
const COMMON_EVENTS = [
"onAnimationEnd",
"onAnimationIteration",
"onAnimationStart",
"onBeforeInput",
"onBlur",
"onChange",
"onClick",
"onContextMenu",
"onCopy",
"onCut",
"onDblClick",
"onDrag",
"onDragEnd",
"onDragEnter",
"onDragExit",
"onDragLeave",
"onDragOver",
"onDragStart",
"onDrop",
"onError",
"onFocus",
"onFocusIn",
"onFocusOut",
"onGotPointerCapture",
"onInput",
"onInvalid",
"onKeyDown",
"onKeyPress",
"onKeyUp",
"onLoad",
"onLostPointerCapture",
"onMouseDown",
"onMouseEnter",
"onMouseLeave",
"onMouseMove",
"onMouseOut",
"onMouseOver",
"onMouseUp",
"onPaste",
"onPointerCancel",
"onPointerDown",
"onPointerEnter",
"onPointerLeave",
"onPointerMove",
"onPointerOut",
"onPointerOver",
"onPointerUp",
"onReset",
"onScroll",
"onSelect",
"onSubmit",
"onToggle",
"onTouchCancel",
"onTouchEnd",
"onTouchMove",
"onTouchStart",
"onTransitionEnd",
"onWheel",
];
const COMMON_EVENTS_MAP = new Map((function* () {
for (const event of COMMON_EVENTS) {
yield [event.toLowerCase(), event];
}
})());
const NONSTANDARD_EVENTS_MAP = {
ondoubleclick: "onDblClick",
};
const isCommonHandlerName = (lowercaseHandlerName) => COMMON_EVENTS_MAP.has(lowercaseHandlerName);
const getCommonEventHandlerName = (lowercaseHandlerName) => COMMON_EVENTS_MAP.get(lowercaseHandlerName);
const isNonstandardEventName = (lowercaseEventName) => Boolean(NONSTANDARD_EVENTS_MAP[lowercaseEventName]);
const getStandardEventHandlerName = (lowercaseEventName) => NONSTANDARD_EVENTS_MAP[lowercaseEventName];
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce naming DOM element event handlers consistently and prevent Solid's analysis from misunderstanding whether a prop should be an event handler.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/event-handlers.md",
},
fixable: "code",
hasSuggestions: true,
schema: [
{
type: "object",
properties: {
ignoreCase: {
type: "boolean",
description: "if true, don't warn on ambiguously named event handlers like `onclick` or `onchange`",
default: false,
},
warnOnSpread: {
type: "boolean",
description: "if true, warn when spreading event handlers onto JSX. Enable for Solid < v1.6.",
default: false,
},
},
additionalProperties: false,
},
],
messages: {
"detected-attr": 'The {{name}} prop is named as an event handler (starts with "on"), but Solid knows its value ({{staticValue}}) is a string or number, so it will be treated as an attribute. If this is intentional, name this prop attr:{{name}}.',
naming: "The {{name}} prop is ambiguous. If it is an event handler, change it to {{handlerName}}. If it is an attribute, change it to {{attrName}}.",
capitalization: "The {{name}} prop should be renamed to {{fixedName}} for readability.",
nonstandard: "The {{name}} prop should be renamed to {{fixedName}}, because it's not a standard event handler.",
"make-handler": "Change the {{name}} prop to {{handlerName}}.",
"make-attr": "Change the {{name}} prop to {{attrName}}.",
"spread-handler": "The {{name}} prop should be added as a JSX attribute, not spread in. Solid doesn't add listeners when spreading into JSX.",
},
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
return {
JSXAttribute(node) {
const openingElement = node.parent;
if (openingElement.name.type !== "JSXIdentifier" ||
!(0, utils_2.isDOMElementName)(openingElement.name.name)) {
return;
}
if (node.name.type === "JSXNamespacedName") {
return;
}
const { name } = node.name;
if (!/^on[a-zA-Z]/.test(name)) {
return;
}
let staticValue = null;
if (node.value?.type === "JSXExpressionContainer" &&
node.value.expression.type !== "JSXEmptyExpression" &&
node.value.expression.type !== "ArrayExpression" &&
(staticValue = getStaticValue(node.value.expression, context.getScope())) !== null &&
(typeof staticValue.value === "string" || typeof staticValue.value === "number")) {
context.report({
node,
messageId: "detected-attr",
data: {
name,
staticValue: staticValue.value,
},
});
}
else if (node.value === null || node.value?.type === "Literal") {
context.report({
node,
messageId: "detected-attr",
data: {
name,
staticValue: node.value !== null ? node.value.value : true,
},
});
}
else if (!context.options[0]?.ignoreCase) {
const lowercaseHandlerName = name.toLowerCase();
if (isNonstandardEventName(lowercaseHandlerName)) {
const fixedName = getStandardEventHandlerName(lowercaseHandlerName);
context.report({
node: node.name,
messageId: "nonstandard",
data: { name, fixedName },
fix: (fixer) => fixer.replaceText(node.name, fixedName),
});
}
else if (isCommonHandlerName(lowercaseHandlerName)) {
const fixedName = getCommonEventHandlerName(lowercaseHandlerName);
if (fixedName !== name) {
context.report({
node: node.name,
messageId: "capitalization",
data: { name, fixedName },
fix: (fixer) => fixer.replaceText(node.name, fixedName),
});
}
}
else if (name[2] === name[2].toLowerCase()) {
const handlerName = `on${name[2].toUpperCase()}${name.slice(3)}`;
const attrName = `attr:${name}`;
context.report({
node: node.name,
messageId: "naming",
data: { name, attrName, handlerName },
suggest: [
{
messageId: "make-handler",
data: { name, handlerName },
fix: (fixer) => fixer.replaceText(node.name, handlerName),
},
{
messageId: "make-attr",
data: { name, attrName },
fix: (fixer) => fixer.replaceText(node.name, attrName),
},
],
});
}
}
},
Property(node) {
if (context.options[0]?.warnOnSpread &&
node.parent?.type === "ObjectExpression" &&
node.parent.parent?.type === "JSXSpreadAttribute" &&
node.parent.parent.parent?.type === "JSXOpeningElement") {
const openingElement = node.parent.parent.parent;
if (openingElement.name.type === "JSXIdentifier" &&
(0, utils_2.isDOMElementName)(openingElement.name.name)) {
if (node.key.type === "Identifier" && /^on/.test(node.key.name)) {
const handlerName = node.key.name;
context.report({
node,
messageId: "spread-handler",
data: {
name: node.key.name,
},
*fix(fixer) {
const commaAfter = sourceCode.getTokenAfter(node);
yield fixer.remove(node.parent.properties.length === 1
? node.parent.parent
: node);
if (commaAfter?.value === ",") {
yield fixer.remove(commaAfter);
}
yield fixer.insertTextAfter(node.parent.parent, ` ${handlerName}={${sourceCode.getText(node.value)}}`);
},
});
}
}
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"prefer-source", [], TSESLint.RuleListener>;
export default _default;

170
node_modules/eslint-plugin-solid/dist/rules/imports.js generated vendored Normal file
View File

@@ -0,0 +1,170 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const primitiveMap = new Map();
for (const primitive of [
"createSignal",
"createEffect",
"createMemo",
"createResource",
"onMount",
"onCleanup",
"onError",
"untrack",
"batch",
"on",
"createRoot",
"getOwner",
"runWithOwner",
"mergeProps",
"splitProps",
"useTransition",
"observable",
"from",
"mapArray",
"indexArray",
"createContext",
"useContext",
"children",
"lazy",
"createUniqueId",
"createDeferred",
"createRenderEffect",
"createComputed",
"createReaction",
"createSelector",
"DEV",
"For",
"Show",
"Switch",
"Match",
"Index",
"ErrorBoundary",
"Suspense",
"SuspenseList",
]) {
primitiveMap.set(primitive, "solid-js");
}
for (const primitive of [
"Portal",
"render",
"hydrate",
"renderToString",
"renderToStream",
"isServer",
"renderToStringAsync",
"generateHydrationScript",
"HydrationScript",
"Dynamic",
]) {
primitiveMap.set(primitive, "solid-js/web");
}
for (const primitive of [
"createStore",
"produce",
"reconcile",
"unwrap",
"createMutable",
"modifyMutable",
]) {
primitiveMap.set(primitive, "solid-js/store");
}
const typeMap = new Map();
for (const type of [
"Signal",
"Accessor",
"Setter",
"Resource",
"ResourceActions",
"ResourceOptions",
"ResourceReturn",
"ResourceFetcher",
"InitializedResourceReturn",
"Component",
"VoidProps",
"VoidComponent",
"ParentProps",
"ParentComponent",
"FlowProps",
"FlowComponent",
"ValidComponent",
"ComponentProps",
"Ref",
"MergeProps",
"SplitPrips",
"Context",
"JSX",
"ResolvedChildren",
"MatchProps",
]) {
typeMap.set(type, "solid-js");
}
for (const type of ["MountableElement"]) {
typeMap.set(type, "solid-js/web");
}
for (const type of ["StoreNode", "Store", "SetStoreFunction"]) {
typeMap.set(type, "solid-js/store");
}
const sourceRegex = /^solid-js(?:\/web|\/store)?$/;
const isSource = (source) => sourceRegex.test(source);
exports.default = createRule({
meta: {
type: "suggestion",
docs: {
description: 'Enforce consistent imports from "solid-js", "solid-js/web", and "solid-js/store".',
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/imports.md",
},
fixable: "code",
schema: [],
messages: {
"prefer-source": 'Prefer importing {{name}} from "{{source}}".',
},
},
defaultOptions: [],
create(context) {
return {
ImportDeclaration(node) {
const source = node.source.value;
if (!isSource(source))
return;
for (const specifier of node.specifiers) {
if (specifier.type === "ImportSpecifier") {
const isType = specifier.importKind === "type" || node.importKind === "type";
const map = isType ? typeMap : primitiveMap;
const correctSource = map.get(specifier.imported.name);
if (correctSource != null && correctSource !== source) {
context.report({
node: specifier,
messageId: "prefer-source",
data: {
name: specifier.imported.name,
source: correctSource,
},
fix(fixer) {
const sourceCode = context.getSourceCode();
const program = sourceCode.ast;
const correctDeclaration = program.body.find((node) => node.type === "ImportDeclaration" && node.source.value === correctSource);
if (correctDeclaration) {
return [
(0, utils_2.removeSpecifier)(fixer, sourceCode, specifier),
(0, utils_2.appendImports)(fixer, sourceCode, correctDeclaration, [
sourceCode.getText(specifier),
]),
].filter(Boolean);
}
const firstSolidDeclaration = program.body.find((node) => node.type === "ImportDeclaration" && isSource(node.source.value));
return [
(0, utils_2.removeSpecifier)(fixer, sourceCode, specifier),
(0, utils_2.insertImports)(fixer, sourceCode, correctSource, [sourceCode.getText(specifier)], firstSolidDeclaration, isType),
];
},
});
}
}
}
},
};
},
});

View File

@@ -0,0 +1,7 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "noDuplicateProps" | "noDuplicateClass" | "noDuplicateChildren";
type Options = [{
ignoreCase?: boolean;
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow passing the same prop twice in JSX.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/jsx-no-duplicate-props.md",
},
schema: [
{
type: "object",
properties: {
ignoreCase: {
type: "boolean",
description: "Consider two prop names differing only by case to be the same.",
default: false,
},
},
},
],
messages: {
noDuplicateProps: "Duplicate props are not allowed.",
noDuplicateClass: "Duplicate `class` props are not allowed; while it might seem to work, it can break unexpectedly. Use `classList` instead.",
noDuplicateChildren: "Using {{used}} at the same time is not allowed.",
},
},
defaultOptions: [],
create(context) {
return {
JSXOpeningElement(node) {
const ignoreCase = context.options[0]?.ignoreCase ?? false;
const props = new Set();
const checkPropName = (name, node) => {
if (ignoreCase || name.startsWith("on")) {
name = name
.toLowerCase()
.replace(/^on(?:capture)?:/, "on")
.replace(/^(?:attr|prop):/, "");
}
if (props.has(name)) {
context.report({
node,
messageId: name === "class" ? "noDuplicateClass" : "noDuplicateProps",
});
}
props.add(name);
};
for (const [name, propNode] of (0, utils_2.jsxGetAllProps)(node.attributes)) {
checkPropName(name, propNode);
}
const hasChildrenProp = props.has("children");
const hasChildren = node.parent.children.length > 0;
const hasInnerHTML = props.has("innerHTML") || props.has("innerhtml");
const hasTextContent = props.has("textContent") || props.has("textContent");
const used = [
hasChildrenProp && "`props.children`",
hasChildren && "JSX children",
hasInnerHTML && "`props.innerHTML`",
hasTextContent && "`props.textContent`",
].filter(Boolean);
if (used.length > 1) {
context.report({
node,
messageId: "noDuplicateChildren",
data: {
used: used.join(", "),
},
});
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"noJSURL", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getStaticValue } = utils_1.ASTUtils;
const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow javascript: URLs.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/jsx-no-script-url.md",
},
schema: [],
messages: {
noJSURL: "For security, don't use javascript: URLs. Use event handlers instead if you can.",
},
},
defaultOptions: [],
create(context) {
return {
JSXAttribute(node) {
if (node.name.type === "JSXIdentifier" && node.value) {
const link = getStaticValue(node.value.type === "JSXExpressionContainer" ? node.value.expression : node.value, context.getScope());
if (link && typeof link.value === "string" && isJavaScriptProtocol.test(link.value)) {
context.report({
node: node.value,
messageId: "noJSURL",
});
}
}
},
};
},
});

View File

@@ -0,0 +1,11 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "undefined" | "customDirectiveUndefined" | "autoImport";
type Options = [
{
allowGlobals?: boolean;
autoImport?: boolean;
typescriptEnabled?: boolean;
}?
];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,164 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const AUTO_COMPONENTS = ["Show", "For", "Index", "Switch", "Match"];
const SOURCE_MODULE = "solid-js";
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow references to undefined variables in JSX. Handles custom directives.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/jsx-no-undef.md",
},
fixable: "code",
schema: [
{
type: "object",
properties: {
allowGlobals: {
type: "boolean",
description: "When true, the rule will consider the global scope when checking for defined components.",
default: false,
},
autoImport: {
type: "boolean",
description: 'Automatically import certain components from `"solid-js"` if they are undefined.',
default: true,
},
typescriptEnabled: {
type: "boolean",
description: "Adjusts behavior not to conflict with TypeScript's type checking.",
default: false,
},
},
additionalProperties: false,
},
],
messages: {
undefined: "'{{identifier}}' is not defined.",
customDirectiveUndefined: "Custom directive '{{identifier}}' is not defined.",
autoImport: "{{imports}} should be imported from '{{source}}'.",
},
},
defaultOptions: [],
create(context) {
const allowGlobals = context.options[0]?.allowGlobals ?? false;
const autoImport = context.options[0]?.autoImport !== false;
const isTypeScriptEnabled = context.options[0]?.typescriptEnabled ?? false;
const missingComponentsSet = new Set();
function checkIdentifierInJSX(node, { isComponent, isCustomDirective, } = {}) {
let scope = context.getScope();
const sourceCode = context.getSourceCode();
const sourceType = sourceCode.ast.sourceType;
const scopeUpperBound = !allowGlobals && sourceType === "module" ? "module" : "global";
const variables = [...scope.variables];
if (node.name === "this") {
return;
}
while (scope.type !== scopeUpperBound && scope.type !== "global" && scope.upper) {
scope = scope.upper;
variables.push(...scope.variables);
}
if (scope.childScopes.length) {
variables.push(...scope.childScopes[0].variables);
if (scope.childScopes[0].childScopes.length) {
variables.push(...scope.childScopes[0].childScopes[0].variables);
}
}
if (variables.find((variable) => variable.name === node.name)) {
return;
}
if (isComponent &&
autoImport &&
AUTO_COMPONENTS.includes(node.name) &&
!missingComponentsSet.has(node.name)) {
missingComponentsSet.add(node.name);
}
else if (isCustomDirective) {
context.report({
node,
messageId: "customDirectiveUndefined",
data: {
identifier: node.name,
},
});
}
else if (!isTypeScriptEnabled) {
context.report({
node,
messageId: "undefined",
data: {
identifier: node.name,
},
});
}
}
return {
JSXOpeningElement(node) {
let n;
switch (node.name.type) {
case "JSXIdentifier":
if (!(0, utils_2.isDOMElementName)(node.name.name)) {
checkIdentifierInJSX(node.name, { isComponent: true });
}
break;
case "JSXMemberExpression":
n = node.name;
do {
n = n.object;
} while (n && n.type !== "JSXIdentifier");
if (n) {
checkIdentifierInJSX(n);
}
break;
default:
break;
}
},
"JSXAttribute > JSXNamespacedName": (node) => {
if (node.namespace?.type === "JSXIdentifier" &&
node.namespace.name === "use" &&
node.name?.type === "JSXIdentifier") {
checkIdentifierInJSX(node.name, { isCustomDirective: true });
}
},
"Program:exit": (programNode) => {
const missingComponents = Array.from(missingComponentsSet.values());
if (autoImport && missingComponents.length) {
const importNode = programNode.body.find((n) => n.type === "ImportDeclaration" &&
n.importKind !== "type" &&
n.source.type === "Literal" &&
n.source.value === SOURCE_MODULE);
if (importNode) {
context.report({
node: importNode,
messageId: "autoImport",
data: {
imports: (0, utils_2.formatList)(missingComponents),
source: SOURCE_MODULE,
},
fix: (fixer) => {
return (0, utils_2.appendImports)(fixer, context.getSourceCode(), importNode, missingComponents);
},
});
}
else {
context.report({
node: programNode,
messageId: "autoImport",
data: {
imports: (0, utils_2.formatList)(missingComponents),
source: SOURCE_MODULE,
},
fix: (fixer) => {
return (0, utils_2.insertImports)(fixer, context.getSourceCode(), "solid-js", missingComponents);
},
});
}
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<never, [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Prevent variables used in JSX from being marked as unused.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/jsx-uses-vars.md",
},
schema: [],
messages: {},
},
defaultOptions: [],
create(context) {
return {
JSXOpeningElement(node) {
let parent;
switch (node.name.type) {
case "JSXNamespacedName":
return;
case "JSXIdentifier":
context.markVariableAsUsed(node.name.name);
break;
case "JSXMemberExpression":
parent = node.name.object;
while (parent?.type === "JSXMemberExpression") {
parent = parent.object;
}
if (parent.type === "JSXIdentifier") {
context.markVariableAsUsed(parent.name);
}
break;
}
},
"JSXAttribute > JSXNamespacedName": (node) => {
if (node.namespace?.type === "JSXIdentifier" &&
node.namespace.name === "use" &&
node.name?.type === "JSXIdentifier") {
context.markVariableAsUsed(node.name.name);
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"noArrayHandlers", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow usage of type-unsafe event handlers.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-array-handlers.md",
},
schema: [],
messages: {
noArrayHandlers: "Passing an array as an event handler is potentially type-unsafe.",
},
},
defaultOptions: [],
create(context) {
return {
JSXAttribute(node) {
const openingElement = node.parent;
if (openingElement.name.type !== "JSXIdentifier" ||
!(0, utils_2.isDOMElementName)(openingElement.name.name)) {
return;
}
const isNamespacedHandler = node.name.type === "JSXNamespacedName" && node.name.namespace.name === "on";
const isNormalEventHandler = node.name.type === "JSXIdentifier" && /^on[a-zA-Z]/.test(node.name.name);
if ((isNamespacedHandler || isNormalEventHandler) &&
node.value?.type === "JSXExpressionContainer" &&
(0, utils_2.trace)(node.value.expression, context.getScope()).type === "ArrayExpression") {
context.report({
node,
messageId: "noArrayHandlers",
});
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"noDestructure", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getStringIfConstant } = utils_1.ASTUtils;
const getName = (node) => {
switch (node.type) {
case "Literal":
return typeof node.value === "string" ? node.value : null;
case "Identifier":
return node.name;
case "AssignmentPattern":
return getName(node.left);
default:
return getStringIfConstant(node);
}
};
const getPropertyInfo = (prop) => {
const valueName = getName(prop.value);
if (valueName !== null) {
return {
real: prop.key,
var: valueName,
computed: prop.computed,
init: prop.value.type === "AssignmentPattern" ? prop.value.right : undefined,
};
}
else {
return null;
}
};
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow destructuring props. In Solid, props must be used with property accesses (`props.foo`) to preserve reactivity. This rule only tracks destructuring in the parameter list.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-destructure.md",
},
fixable: "code",
schema: [],
messages: {
noDestructure: "Destructuring component props breaks Solid's reactivity; use property access instead.",
},
},
defaultOptions: [],
create(context) {
const functionStack = [];
const currentFunction = () => functionStack[functionStack.length - 1];
const onFunctionEnter = () => {
functionStack.push({ hasJSX: false });
};
const onFunctionExit = (node) => {
if (node.params.length === 1) {
const props = node.params[0];
if (props.type === "ObjectPattern" &&
currentFunction().hasJSX &&
node.parent?.type !== "JSXExpressionContainer") {
context.report({
node: props,
messageId: "noDestructure",
fix: (fixer) => fixDestructure(node, props, fixer),
});
}
}
functionStack.pop();
};
function* fixDestructure(func, props, fixer) {
const propsName = "props";
const properties = props.properties;
const propertyInfo = [];
let rest = null;
for (const property of properties) {
if (property.type === "RestElement") {
rest = property;
}
else {
const info = getPropertyInfo(property);
if (info === null) {
continue;
}
propertyInfo.push(info);
}
}
const hasDefaults = propertyInfo.some((info) => info.init);
const origProps = !(hasDefaults || rest) ? propsName : "_" + propsName;
if (props.typeAnnotation) {
const range = [props.range[0], props.typeAnnotation.range[0]];
yield fixer.replaceTextRange(range, origProps);
}
else {
yield fixer.replaceText(props, origProps);
}
const sourceCode = context.getSourceCode();
const defaultsObjectString = () => propertyInfo
.filter((info) => info.init)
.map((info) => `${info.computed ? "[" : ""}${sourceCode.getText(info.real)}${info.computed ? "]" : ""}: ${sourceCode.getText(info.init)}`)
.join(", ");
const splitPropsArray = () => `[${propertyInfo
.map((info) => info.real.type === "Identifier"
? JSON.stringify(info.real.name)
: sourceCode.getText(info.real))
.join(", ")}]`;
let lineToInsert = "";
if (hasDefaults && rest) {
lineToInsert = ` const [${propsName}, ${(rest.argument.type === "Identifier" && rest.argument.name) || "rest"}] = splitProps(mergeProps({ ${defaultsObjectString()} }, ${origProps}), ${splitPropsArray()});`;
}
else if (hasDefaults) {
lineToInsert = ` const ${propsName} = mergeProps({ ${defaultsObjectString()} }, ${origProps});\n`;
}
else if (rest) {
lineToInsert = ` const [${propsName}, ${(rest.argument.type === "Identifier" && rest.argument.name) || "rest"}] = splitProps(${origProps}, ${splitPropsArray()});\n`;
}
if (lineToInsert) {
const body = func.body;
if (body.type === "BlockStatement") {
if (body.body.length > 0) {
yield fixer.insertTextBefore(body.body[0], lineToInsert);
}
}
else {
const maybeOpenParen = sourceCode.getTokenBefore(body);
if (maybeOpenParen?.value === "(") {
yield fixer.remove(maybeOpenParen);
}
const maybeCloseParen = sourceCode.getTokenAfter(body);
if (maybeCloseParen?.value === ")") {
yield fixer.remove(maybeCloseParen);
}
yield fixer.insertTextBefore(body, `{\n${lineToInsert} return (`);
yield fixer.insertTextAfter(body, `);\n}`);
}
}
const scope = sourceCode.scopeManager?.acquire(func);
if (scope) {
for (const [info, variable] of propertyInfo.map((info) => [info, scope.set.get(info.var)])) {
if (variable) {
for (const reference of variable.references) {
if (reference.isReadOnly()) {
const access = info.real.type === "Identifier" && !info.computed
? `.${info.real.name}`
: `[${sourceCode.getText(info.real)}]`;
yield fixer.replaceText(reference.identifier, `${propsName}${access}`);
}
}
}
}
}
}
return {
FunctionDeclaration: onFunctionEnter,
FunctionExpression: onFunctionEnter,
ArrowFunctionExpression: onFunctionEnter,
"FunctionDeclaration:exit": onFunctionExit,
"FunctionExpression:exit": onFunctionExit,
"ArrowFunctionExpression:exit": onFunctionExit,
JSXElement() {
if (functionStack.length) {
currentFunction().hasJSX = true;
}
},
JSXFragment() {
if (functionStack.length) {
currentFunction().hasJSX = true;
}
},
};
},
});

View File

@@ -0,0 +1,7 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "dangerous" | "conflict" | "notHtml" | "useInnerText" | "dangerouslySetInnerHTML";
type Options = [{
allowStatic?: boolean;
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,127 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const is_html_1 = __importDefault(require("is-html"));
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getStringIfConstant } = utils_1.ASTUtils;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow usage of the innerHTML attribute, which can often lead to security vulnerabilities.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-innerhtml.md",
},
fixable: "code",
hasSuggestions: true,
schema: [
{
type: "object",
properties: {
allowStatic: {
description: "if the innerHTML value is guaranteed to be a static HTML string (i.e. no user input), allow it",
type: "boolean",
default: true,
},
},
additionalProperties: false,
},
],
messages: {
dangerous: "The innerHTML attribute is dangerous; passing unsanitized input can lead to security vulnerabilities.",
conflict: "The innerHTML attribute should not be used on an element with child elements; they will be overwritten.",
notHtml: "The string passed to innerHTML does not appear to be valid HTML.",
useInnerText: "For text content, using innerText is clearer and safer.",
dangerouslySetInnerHTML: "The dangerouslySetInnerHTML prop is not supported; use innerHTML instead.",
},
},
defaultOptions: [{ allowStatic: true }],
create(context) {
const allowStatic = Boolean(context.options[0]?.allowStatic ?? true);
return {
JSXAttribute(node) {
if ((0, utils_2.jsxPropName)(node) === "dangerouslySetInnerHTML") {
if (node.value?.type === "JSXExpressionContainer" &&
node.value.expression.type === "ObjectExpression" &&
node.value.expression.properties.length === 1) {
const htmlProp = node.value.expression.properties[0];
if (htmlProp.type === "Property" &&
htmlProp.key.type === "Identifier" &&
htmlProp.key.name === "__html") {
context.report({
node,
messageId: "dangerouslySetInnerHTML",
fix: (fixer) => {
const propRange = node.range;
const valueRange = htmlProp.value.range;
return [
fixer.replaceTextRange([propRange[0], valueRange[0]], "innerHTML={"),
fixer.replaceTextRange([valueRange[1], propRange[1]], "}"),
];
},
});
}
else {
context.report({
node,
messageId: "dangerouslySetInnerHTML",
});
}
}
else {
context.report({
node,
messageId: "dangerouslySetInnerHTML",
});
}
return;
}
else if ((0, utils_2.jsxPropName)(node) !== "innerHTML") {
return;
}
if (allowStatic) {
const innerHtmlNode = node.value?.type === "JSXExpressionContainer" ? node.value.expression : node.value;
const innerHtml = innerHtmlNode && getStringIfConstant(innerHtmlNode);
if (typeof innerHtml === "string") {
if ((0, is_html_1.default)(innerHtml)) {
if (node.parent?.parent?.type === "JSXElement" &&
node.parent.parent.children?.length) {
context.report({
node: node.parent.parent,
messageId: "conflict",
});
}
}
else {
context.report({
node,
messageId: "notHtml",
suggest: [
{
fix: (fixer) => fixer.replaceText(node.name, "innerText"),
messageId: "useInnerText",
},
],
});
}
}
else {
context.report({
node,
messageId: "dangerous",
});
}
}
else {
context.report({
node,
messageId: "dangerous",
});
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"mergeProps" | "noStore" | "spreadCall" | "spreadMember" | "proxyLiteral", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow usage of APIs that use ES6 Proxies, only to target environments that don't support them.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-proxy-apis.md",
},
schema: [],
messages: {
noStore: "Solid Store APIs use Proxies, which are incompatible with your target environment.",
spreadCall: "Using a function call in JSX spread makes Solid use Proxies, which are incompatible with your target environment.",
spreadMember: "Using a property access in JSX spread makes Solid use Proxies, which are incompatible with your target environment.",
proxyLiteral: "Proxies are incompatible with your target environment.",
mergeProps: "If you pass a function to `mergeProps`, it will create a Proxy, which are incompatible with your target environment.",
},
},
defaultOptions: [],
create(context) {
const { matchImport, handleImportDeclaration } = (0, utils_2.trackImports)();
return {
ImportDeclaration(node) {
handleImportDeclaration(node);
const source = node.source.value;
if (source === "solid-js/store") {
context.report({
node,
messageId: "noStore",
});
}
},
"JSXSpreadAttribute MemberExpression"(node) {
context.report({ node, messageId: "spreadMember" });
},
"JSXSpreadAttribute CallExpression"(node) {
context.report({ node, messageId: "spreadCall" });
},
CallExpression(node) {
if (node.callee.type === "Identifier") {
if (matchImport("mergeProps", node.callee.name)) {
node.arguments
.filter((arg) => {
if (arg.type === "SpreadElement")
return true;
const traced = (0, utils_2.trace)(arg, context.getScope());
return ((traced.type === "Identifier" && !(0, utils_2.isPropsByName)(traced.name)) ||
(0, utils_2.isFunctionNode)(traced));
})
.forEach((badArg) => {
context.report({
node: badArg,
messageId: "mergeProps",
});
});
}
}
else if (node.callee.type === "MemberExpression") {
if (node.callee.object.type === "Identifier" &&
node.callee.object.name === "Proxy" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "revocable") {
context.report({
node,
messageId: "proxyLiteral",
});
}
}
},
NewExpression(node) {
if (node.callee.type === "Identifier" && node.callee.name === "Proxy") {
context.report({ node, messageId: "proxyLiteral" });
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"noUselessDep", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow usage of dependency arrays in `createEffect` and `createMemo`.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-react-deps.md",
},
fixable: "code",
schema: [],
messages: {
noUselessDep: "In Solid, `{{name}}` doesn't accept a dependency array because it automatically tracks its dependencies. If you really need to override the list of dependencies, use `on`.",
},
},
defaultOptions: [],
create(context) {
const { matchImport, handleImportDeclaration } = (0, utils_2.trackImports)();
return {
ImportDeclaration: handleImportDeclaration,
CallExpression(node) {
if (node.callee.type === "Identifier" &&
matchImport(["createEffect", "createMemo"], node.callee.name) &&
node.arguments.length === 2 &&
node.arguments.every((arg) => arg.type !== "SpreadElement")) {
const [arg0, arg1] = node.arguments.map((arg) => (0, utils_2.trace)(arg, context.getScope()));
if ((0, utils_2.isFunctionNode)(arg0) && arg0.params.length === 0 && arg1.type === "ArrayExpression") {
context.report({
node: node.arguments[1],
messageId: "noUselessDep",
data: {
name: node.callee.name,
},
fix: arg1 === node.arguments[1] ? (fixer) => fixer.remove(arg1) : undefined,
});
}
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"prefer" | "noUselessKey", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const reactSpecificProps = [
{ from: "className", to: "class" },
{ from: "htmlFor", to: "for" },
];
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Disallow usage of React-specific `className`/`htmlFor` props, which were deprecated in v1.4.0.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-react-specific-props.md",
},
fixable: "code",
schema: [],
messages: {
prefer: "Prefer the `{{ to }}` prop over the deprecated `{{ from }}` prop.",
noUselessKey: "Elements in a <For> or <Index> list do not need a key prop.",
},
},
defaultOptions: [],
create(context) {
return {
JSXOpeningElement(node) {
for (const { from, to } of reactSpecificProps) {
const classNameAttribute = (0, utils_2.jsxGetProp)(node.attributes, from);
if (classNameAttribute) {
const fix = !(0, utils_2.jsxHasProp)(node.attributes, to)
? (fixer) => fixer.replaceText(classNameAttribute.name, to)
: undefined;
context.report({
node: classNameAttribute,
messageId: "prefer",
data: { from, to },
fix,
});
}
}
if (node.name.type === "JSXIdentifier" && (0, utils_2.isDOMElementName)(node.name.name)) {
const keyProp = (0, utils_2.jsxGetProp)(node.attributes, "key");
if (keyProp) {
context.report({
node: keyProp,
messageId: "noUselessKey",
fix: (fixer) => fixer.remove(keyProp),
});
}
}
},
};
},
});

View File

@@ -0,0 +1,7 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "unknown" | "style" | "component" | "component-suggest";
type Options = [{
allowedNamespaces: Array<string>;
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,87 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const knownNamespaces = ["on", "oncapture", "use", "prop", "attr"];
const styleNamespaces = ["style", "class"];
const otherNamespaces = ["xmlns", "xlink"];
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce using only Solid-specific namespaced attribute names (i.e. `'on:'` in `<div on:click={...} />`).",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/no-unknown-namespaces.md",
},
hasSuggestions: true,
schema: [
{
type: "object",
properties: {
allowedNamespaces: {
description: "an array of additional namespace names to allow",
type: "array",
items: {
type: "string",
},
default: [],
minItems: 1,
uniqueItems: true,
},
},
additionalProperties: false,
},
],
messages: {
unknown: `'{{namespace}}:' is not one of Solid's special prefixes for JSX attributes (${knownNamespaces
.map((n) => `'${n}:'`)
.join(", ")}).`,
style: "Using the '{{namespace}}:' special prefix is potentially confusing, prefer the '{{namespace}}' prop instead.",
component: "Namespaced props have no effect on components.",
"component-suggest": "Replace {{namespace}}:{{name}} with {{name}}.",
},
},
defaultOptions: [],
create(context) {
const explicitlyAllowedNamespaces = context.options?.[0]?.allowedNamespaces;
return {
"JSXAttribute > JSXNamespacedName": (node) => {
const openingElement = node.parent.parent;
if (openingElement.name.type === "JSXIdentifier" &&
!(0, utils_2.isDOMElementName)(openingElement.name.name)) {
context.report({
node,
messageId: "component",
suggest: [
{
messageId: "component-suggest",
data: { namespace: node.namespace.name, name: node.name.name },
fix: (fixer) => fixer.replaceText(node, node.name.name),
},
],
});
return;
}
const namespace = node.namespace?.name;
if (!(knownNamespaces.includes(namespace) ||
otherNamespaces.includes(namespace) ||
explicitlyAllowedNamespaces?.includes(namespace))) {
if (styleNamespaces.includes(namespace)) {
context.report({
node,
messageId: "style",
data: { namespace },
});
}
else {
context.report({
node,
messageId: "unknown",
data: { namespace },
});
}
}
},
};
},
});

View File

@@ -0,0 +1,6 @@
import type { TSESLint } from "@typescript-eslint/utils";
type Options = [{
classnames?: Array<string>;
}?];
declare const _default: TSESLint.RuleModule<"preferClasslist", Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce using the classlist prop over importing a classnames helper. The classlist prop accepts an object `{ [class: string]: boolean }` just like classnames.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/prefer-classlist.md",
},
fixable: "code",
schema: [
{
type: "object",
properties: {
classnames: {
type: "array",
description: "An array of names to treat as `classnames` functions",
default: ["cn", "clsx", "classnames"],
items: {
type: "string",
},
minItems: 1,
uniqueItems: true,
},
},
additionalProperties: false,
},
],
messages: {
preferClasslist: "The classlist prop should be used instead of {{ classnames }} to efficiently set classes based on an object.",
},
deprecated: true,
},
defaultOptions: [],
create(context) {
const classnames = context.options[0]?.classnames ?? ["cn", "clsx", "classnames"];
return {
JSXAttribute(node) {
if (["class", "className"].indexOf((0, utils_2.jsxPropName)(node)) === -1 ||
(0, utils_2.jsxHasProp)(node.parent?.attributes ?? [], "classlist")) {
return;
}
if (node.value?.type === "JSXExpressionContainer") {
const expr = node.value.expression;
if (expr.type === "CallExpression" &&
expr.callee.type === "Identifier" &&
classnames.indexOf(expr.callee.name) !== -1 &&
expr.arguments.length === 1 &&
expr.arguments[0].type === "ObjectExpression") {
context.report({
node,
messageId: "preferClasslist",
data: {
classnames: expr.callee.name,
},
fix: (fixer) => {
const attrRange = node.range;
const objectRange = expr.arguments[0].range;
return [
fixer.replaceTextRange([attrRange[0], objectRange[0]], "classlist={"),
fixer.replaceTextRange([objectRange[1], attrRange[1]], "}"),
];
},
});
}
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"preferFor" | "preferForOrIndex", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getPropertyName } = utils_1.ASTUtils;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce using Solid's `<For />` component for mapping an array to JSX elements.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/prefer-for.md",
},
fixable: "code",
schema: [],
messages: {
preferFor: "Use Solid's `<For />` component for efficiently rendering lists. Array#map causes DOM elements to be recreated.",
preferForOrIndex: "Use Solid's `<For />` component or `<Index />` component for rendering lists. Array#map causes DOM elements to be recreated.",
},
},
defaultOptions: [],
create(context) {
const reportPreferFor = (node) => {
const jsxExpressionContainerNode = node.parent;
const arrayNode = node.callee.object;
const mapFnNode = node.arguments[0];
context.report({
node,
messageId: "preferFor",
fix: (fixer) => {
const beforeArray = [
jsxExpressionContainerNode.range[0],
arrayNode.range[0],
];
const betweenArrayAndMapFn = [arrayNode.range[1], mapFnNode.range[0]];
const afterMapFn = [
mapFnNode.range[1],
jsxExpressionContainerNode.range[1],
];
return [
fixer.replaceTextRange(beforeArray, "<For each={"),
fixer.replaceTextRange(betweenArrayAndMapFn, "}>{"),
fixer.replaceTextRange(afterMapFn, "}</For>"),
];
},
});
};
return {
CallExpression(node) {
const callOrChain = node.parent?.type === "ChainExpression" ? node.parent : node;
if (callOrChain.parent?.type === "JSXExpressionContainer" &&
(0, utils_2.isJSXElementOrFragment)(callOrChain.parent.parent)) {
if (node.callee.type === "MemberExpression" &&
getPropertyName(node.callee) === "map" &&
node.arguments.length === 1 &&
(0, utils_2.isFunctionNode)(node.arguments[0])) {
const mapFnNode = node.arguments[0];
if (mapFnNode.params.length === 1 && mapFnNode.params[0].type !== "RestElement") {
reportPreferFor(node);
}
else {
context.report({
node,
messageId: "preferForOrIndex",
});
}
}
}
},
};
},
});

View File

@@ -0,0 +1,3 @@
import type { TSESLint } from "@typescript-eslint/utils";
declare const _default: TSESLint.RuleModule<"preferShowAnd" | "preferShowTernary", [], TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const EXPENSIVE_TYPES = ["JSXElement", "JSXFragment", "Identifier"];
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce using Solid's `<Show />` component for conditionally showing content. Solid's compiler covers this case, so it's a stylistic rule only.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/prefer-show.md",
},
fixable: "code",
schema: [],
messages: {
preferShowAnd: "Use Solid's `<Show />` component for conditionally showing content.",
preferShowTernary: "Use Solid's `<Show />` component for conditionally showing content with a fallback.",
},
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const putIntoJSX = (node) => {
const text = sourceCode.getText(node);
return (0, utils_2.isJSXElementOrFragment)(node) ? text : `{${text}}`;
};
const logicalExpressionHandler = (node) => {
if (node.operator === "&&" && EXPENSIVE_TYPES.includes(node.right.type)) {
context.report({
node,
messageId: "preferShowAnd",
fix: (fixer) => fixer.replaceText(node.parent?.type === "JSXExpressionContainer" &&
(0, utils_2.isJSXElementOrFragment)(node.parent.parent)
? node.parent
: node, `<Show when={${sourceCode.getText(node.left)}}>${putIntoJSX(node.right)}</Show>`),
});
}
};
const conditionalExpressionHandler = (node) => {
if (EXPENSIVE_TYPES.includes(node.consequent.type) ||
EXPENSIVE_TYPES.includes(node.alternate.type)) {
context.report({
node,
messageId: "preferShowTernary",
fix: (fixer) => fixer.replaceText(node.parent?.type === "JSXExpressionContainer" &&
(0, utils_2.isJSXElementOrFragment)(node.parent.parent)
? node.parent
: node, `<Show when={${sourceCode.getText(node.test)}} fallback={${sourceCode.getText(node.alternate)}}>${putIntoJSX(node.consequent)}</Show>`),
});
}
};
return {
JSXExpressionContainer(node) {
if (!(0, utils_2.isJSXElementOrFragment)(node.parent)) {
return;
}
if (node.expression.type === "LogicalExpression") {
logicalExpressionHandler(node.expression);
}
else if (node.expression.type === "ArrowFunctionExpression" &&
node.expression.body.type === "LogicalExpression") {
logicalExpressionHandler(node.expression.body);
}
else if (node.expression.type === "ConditionalExpression") {
conditionalExpressionHandler(node.expression);
}
else if (node.expression.type === "ArrowFunctionExpression" &&
node.expression.body.type === "ConditionalExpression") {
conditionalExpressionHandler(node.expression.body);
}
},
};
},
});

View File

@@ -0,0 +1,7 @@
import { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "noWrite" | "untrackedReactive" | "expectedFunctionGotExpression" | "badSignal" | "badUnnamedDerivedSignal" | "shouldDestructure" | "shouldAssign" | "noAsyncTrackedScope";
type Options = [{
customReactiveFunctions: string[];
}];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,831 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const estraverse_1 = require("estraverse");
const utils_2 = require("../utils");
const { findVariable, getFunctionHeadLocation } = utils_1.ASTUtils;
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
class ScopeStackItem {
node;
trackedScopes = [];
unnamedDerivedSignals = new Set();
hasJSX = false;
constructor(node) {
this.node = node;
}
}
class ScopeStack extends Array {
currentScope = () => this[this.length - 1];
parentScope = () => this[this.length - 2];
pushSignal(variable, declarationScope = this.currentScope().node) {
this.signals.push({
references: variable.references.filter((reference) => !reference.init),
variable,
declarationScope,
});
}
pushUniqueSignal(variable, declarationScope) {
const foundSignal = this.signals.find((s) => s.variable === variable);
if (!foundSignal) {
this.pushSignal(variable, declarationScope);
}
else {
foundSignal.declarationScope = this.findDeepestDeclarationScope(foundSignal.declarationScope, declarationScope);
}
}
pushProps(variable, declarationScope = this.currentScope().node) {
this.props.push({
references: variable.references.filter((reference) => !reference.init),
variable,
declarationScope,
});
}
syncCallbacks = new Set();
*consumeSignalReferencesInScope() {
yield* this.consumeReferencesInScope(this.signals);
this.signals = this.signals.filter((variable) => variable.references.length !== 0);
}
*consumePropsReferencesInScope() {
yield* this.consumeReferencesInScope(this.props);
this.props = this.props.filter((variable) => variable.references.length !== 0);
}
*consumeReferencesInScope(variables) {
for (const variable of variables) {
const { references } = variable;
const inScope = [], notInScope = [];
references.forEach((reference) => {
if (this.isReferenceInCurrentScope(reference)) {
inScope.push(reference);
}
else {
notInScope.push(reference);
}
});
yield* inScope.map((reference) => ({
reference,
declarationScope: variable.declarationScope,
}));
variable.references = notInScope;
}
}
findDeepestDeclarationScope = (a, b) => {
if (a === b)
return a;
for (let i = this.length - 1; i >= 0; i -= 1) {
const { node } = this[i];
if (a === node || b === node) {
return node;
}
}
throw new Error("This should never happen");
};
isReferenceInCurrentScope(reference) {
let parentFunction = (0, utils_2.findParent)(reference.identifier, utils_2.isProgramOrFunctionNode);
while ((0, utils_2.isFunctionNode)(parentFunction) && this.syncCallbacks.has(parentFunction)) {
parentFunction = (0, utils_2.findParent)(parentFunction, utils_2.isProgramOrFunctionNode);
}
return parentFunction === this.currentScope().node;
}
signals = [];
props = [];
}
const getNthDestructuredVar = (id, n, scope) => {
if (id?.type === "ArrayPattern") {
const el = id.elements[n];
if (el?.type === "Identifier") {
return findVariable(scope, el.name);
}
}
return null;
};
const getReturnedVar = (id, scope) => {
if (id.type === "Identifier") {
return findVariable(scope, id.name);
}
return null;
};
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce that reactivity (props, signals, memos, etc.) is properly used, so changes in those values will be tracked and update the view as expected.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/reactivity.md",
},
schema: [
{
type: "object",
properties: {
customReactiveFunctions: {
description: "List of function names to consider as reactive functions (allow signals to be safely passed as arguments). In addition, any create* or use* functions are automatically included.",
type: "array",
items: {
type: "string",
},
default: [],
},
},
additionalProperties: false,
},
],
messages: {
noWrite: "The reactive variable '{{name}}' should not be reassigned or altered directly.",
untrackedReactive: "The reactive variable '{{name}}' should be used within JSX, a tracked scope (like createEffect), or inside an event handler function, or else changes will be ignored.",
expectedFunctionGotExpression: "The reactive variable '{{name}}' should be wrapped in a function for reactivity. This includes event handler bindings on native elements, which are not reactive like other JSX props.",
badSignal: "The reactive variable '{{name}}' should be called as a function when used in {{where}}.",
badUnnamedDerivedSignal: "This function should be passed to a tracked scope (like createEffect) or an event handler because it contains reactivity, or else changes will be ignored.",
shouldDestructure: "For proper analysis, array destructuring should be used to capture the {{nth}}result of this function call.",
shouldAssign: "For proper analysis, a variable should be used to capture the result of this function call.",
noAsyncTrackedScope: "This tracked scope should not be async. Solid's reactivity only tracks synchronously.",
},
},
defaultOptions: [
{
customReactiveFunctions: [],
},
],
create(context, [options]) {
const warnShouldDestructure = (node, nth) => context.report({
node,
messageId: "shouldDestructure",
data: nth ? { nth: nth + " " } : undefined,
});
const warnShouldAssign = (node) => context.report({ node, messageId: "shouldAssign" });
const sourceCode = context.getSourceCode();
const scopeStack = new ScopeStack();
const { currentScope, parentScope } = scopeStack;
const { matchImport, handleImportDeclaration } = (0, utils_2.trackImports)();
const markPropsOnCondition = (node, cb) => {
if (node.params.length === 1 &&
node.params[0].type === "Identifier" &&
node.parent?.type !== "JSXExpressionContainer" &&
node.parent?.type !== "TemplateLiteral" &&
cb(node.params[0])) {
const propsParam = findVariable(context.getScope(), node.params[0]);
if (propsParam) {
scopeStack.pushProps(propsParam, node);
}
}
};
const onFunctionEnter = (node) => {
if ((0, utils_2.isFunctionNode)(node)) {
if (scopeStack.syncCallbacks.has(node)) {
return;
}
markPropsOnCondition(node, (props) => (0, utils_2.isPropsByName)(props.name));
}
scopeStack.push(new ScopeStackItem(node));
};
const matchTrackedScope = (trackedScope, node) => {
switch (trackedScope.expect) {
case "function":
case "called-function":
return node === trackedScope.node;
case "expression":
return Boolean((0, utils_2.findInScope)(node, currentScope().node, (node) => node === trackedScope.node));
}
};
const handleTrackedScopes = (identifier, declarationScope) => {
const currentScopeNode = currentScope().node;
if (!currentScope().trackedScopes.find((trackedScope) => matchTrackedScope(trackedScope, identifier))) {
const matchedExpression = currentScope().trackedScopes.find((trackedScope) => matchTrackedScope({ ...trackedScope, expect: "expression" }, identifier));
if (declarationScope === currentScopeNode) {
let parentMemberExpression = null;
if (identifier.parent?.type === "MemberExpression") {
parentMemberExpression = identifier.parent;
while (parentMemberExpression.parent?.type === "MemberExpression") {
parentMemberExpression = parentMemberExpression.parent;
}
}
const parentCallExpression = identifier.parent?.type === "CallExpression" ? identifier.parent : null;
context.report({
node: parentMemberExpression ?? parentCallExpression ?? identifier,
messageId: matchedExpression ? "expectedFunctionGotExpression" : "untrackedReactive",
data: {
name: parentMemberExpression
? sourceCode.getText(parentMemberExpression)
: identifier.name,
},
});
}
else {
if (!parentScope() || !(0, utils_2.isFunctionNode)(currentScopeNode)) {
throw new Error("this shouldn't happen!");
}
const pushUnnamedDerivedSignal = () => (parentScope().unnamedDerivedSignals ??= new Set()).add(currentScopeNode);
if (currentScopeNode.type === "FunctionDeclaration") {
const functionVariable = sourceCode.scopeManager?.getDeclaredVariables(currentScopeNode)?.[0];
if (functionVariable) {
scopeStack.pushUniqueSignal(functionVariable, declarationScope);
}
else {
pushUnnamedDerivedSignal();
}
}
else if (currentScopeNode.parent?.type === "VariableDeclarator") {
const declarator = currentScopeNode.parent;
const functionVariable = sourceCode.scopeManager?.getDeclaredVariables(declarator)?.[0];
if (functionVariable) {
scopeStack.pushUniqueSignal(functionVariable, declarationScope);
}
else {
pushUnnamedDerivedSignal();
}
}
else if (currentScopeNode.parent?.type === "Property") {
}
else {
pushUnnamedDerivedSignal();
}
}
}
};
const onFunctionExit = (currentScopeNode) => {
if ((0, utils_2.isFunctionNode)(currentScopeNode)) {
markPropsOnCondition(currentScopeNode, (props) => {
if (!(0, utils_2.isPropsByName)(props.name) &&
currentScope().hasJSX) {
const functionName = (0, utils_2.getFunctionName)(currentScopeNode);
if (functionName && !/^[a-z]/.test(functionName))
return true;
}
return false;
});
}
if ((0, utils_2.isFunctionNode)(currentScopeNode) && scopeStack.syncCallbacks.has(currentScopeNode)) {
return;
}
for (const { reference, declarationScope } of scopeStack.consumeSignalReferencesInScope()) {
const identifier = reference.identifier;
if (reference.isWrite()) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (identifier.type === "Identifier") {
const reportBadSignal = (where) => context.report({
node: identifier,
messageId: "badSignal",
data: { name: identifier.name, where },
});
if (identifier.parent?.type === "CallExpression" ||
(identifier.parent?.type === "ArrayExpression" &&
identifier.parent.parent?.type === "CallExpression")) {
handleTrackedScopes(identifier, declarationScope);
}
else if (identifier.parent?.type === "TemplateLiteral") {
reportBadSignal("template literals");
}
else if (identifier.parent?.type === "BinaryExpression" &&
[
"<",
"<=",
">",
">=",
"<<",
">>",
">>>",
"+",
"-",
"*",
"/",
"%",
"**",
"|",
"^",
"&",
"in",
].includes(identifier.parent.operator)) {
reportBadSignal("arithmetic or comparisons");
}
else if (identifier.parent?.type === "UnaryExpression" &&
["-", "+", "~"].includes(identifier.parent.operator)) {
reportBadSignal("unary expressions");
}
else if (identifier.parent?.type === "MemberExpression" &&
identifier.parent.computed &&
identifier.parent.property === identifier) {
reportBadSignal("property accesses");
}
else if (identifier.parent?.type === "JSXExpressionContainer" &&
!currentScope().trackedScopes.find((trackedScope) => trackedScope.node === identifier &&
(trackedScope.expect === "function" || trackedScope.expect === "called-function"))) {
const elementOrAttribute = identifier.parent.parent;
if ((0, utils_2.isJSXElementOrFragment)(elementOrAttribute) ||
(elementOrAttribute?.type === "JSXAttribute" &&
elementOrAttribute.parent?.type === "JSXOpeningElement" &&
elementOrAttribute.parent.name.type === "JSXIdentifier" &&
(0, utils_2.isDOMElementName)(elementOrAttribute.parent.name.name))) {
reportBadSignal("JSX");
}
}
}
}
for (const { reference, declarationScope } of scopeStack.consumePropsReferencesInScope()) {
const identifier = reference.identifier;
if (reference.isWrite()) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (identifier.parent?.type === "MemberExpression" &&
identifier.parent.object === identifier) {
const { parent } = identifier;
if (parent.parent?.type === "AssignmentExpression" && parent.parent.left === parent) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (parent.property.type === "Identifier" &&
/^(?:initial|default|static)[A-Z]/.test(parent.property.name)) {
}
else {
handleTrackedScopes(identifier, declarationScope);
}
}
else if (identifier.parent?.type === "AssignmentExpression" ||
identifier.parent?.type === "VariableDeclarator") {
context.report({
node: identifier,
messageId: "untrackedReactive",
data: { name: identifier.name },
});
}
}
const { unnamedDerivedSignals } = currentScope();
if (unnamedDerivedSignals) {
for (const node of unnamedDerivedSignals) {
if (!currentScope().trackedScopes.find((trackedScope) => matchTrackedScope(trackedScope, node))) {
context.report({
loc: getFunctionHeadLocation(node, sourceCode),
messageId: "badUnnamedDerivedSignal",
});
}
}
}
scopeStack.pop();
};
const checkForSyncCallbacks = (node) => {
if (node.arguments.length === 1 &&
(0, utils_2.isFunctionNode)(node.arguments[0]) &&
!node.arguments[0].async) {
if (node.callee.type === "Identifier" &&
matchImport(["batch", "produce"], node.callee.name)) {
scopeStack.syncCallbacks.add(node.arguments[0]);
}
else if (node.callee.type === "MemberExpression" &&
!node.callee.computed &&
node.callee.object.type !== "ObjectExpression" &&
/^(?:forEach|map|flatMap|reduce|reduceRight|find|findIndex|filter|every|some)$/.test(node.callee.property.name)) {
scopeStack.syncCallbacks.add(node.arguments[0]);
}
}
if (node.callee.type === "Identifier") {
if (matchImport(["createSignal", "createStore"], node.callee.name) &&
node.parent?.type === "VariableDeclarator") {
const setter = getNthDestructuredVar(node.parent.id, 1, context.getScope());
if (setter) {
for (const reference of setter.references) {
const { identifier } = reference;
if (!reference.init &&
reference.isRead() &&
identifier.parent?.type === "CallExpression") {
for (const arg of identifier.parent.arguments) {
if ((0, utils_2.isFunctionNode)(arg) && !arg.async) {
scopeStack.syncCallbacks.add(arg);
}
}
}
}
}
}
else if (matchImport(["mapArray", "indexArray"], node.callee.name)) {
const arg1 = node.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1)) {
scopeStack.syncCallbacks.add(arg1);
}
}
}
if ((0, utils_2.isFunctionNode)(node.callee)) {
scopeStack.syncCallbacks.add(node.callee);
}
};
const checkForReactiveAssignment = (id, init) => {
init = (0, utils_2.ignoreTransparentWrappers)(init);
if (init.type === "CallExpression" && init.callee.type === "Identifier") {
const { callee } = init;
if (matchImport(["createSignal", "useTransition"], callee.name)) {
const signal = id && getNthDestructuredVar(id, 0, context.getScope());
if (signal) {
scopeStack.pushSignal(signal, currentScope().node);
}
else {
warnShouldDestructure(id ?? init, "first");
}
}
else if (matchImport(["createMemo", "createSelector"], callee.name)) {
const memo = id && getReturnedVar(id, context.getScope());
if (memo) {
scopeStack.pushSignal(memo, currentScope().node);
}
else {
warnShouldAssign(id ?? init);
}
}
else if (matchImport("createStore", callee.name)) {
const store = id && getNthDestructuredVar(id, 0, context.getScope());
if (store) {
scopeStack.pushProps(store, currentScope().node);
}
else {
warnShouldDestructure(id ?? init, "first");
}
}
else if (matchImport("mergeProps", callee.name)) {
const merged = id && getReturnedVar(id, context.getScope());
if (merged) {
scopeStack.pushProps(merged, currentScope().node);
}
else {
warnShouldAssign(id ?? init);
}
}
else if (matchImport("splitProps", callee.name)) {
if (id?.type === "ArrayPattern") {
const vars = id.elements
.map((_, i) => getNthDestructuredVar(id, i, context.getScope()))
.filter(Boolean);
if (vars.length === 0) {
warnShouldDestructure(id);
}
else {
vars.forEach((variable) => {
scopeStack.pushProps(variable, currentScope().node);
});
}
}
else {
const vars = id && getReturnedVar(id, context.getScope());
if (vars) {
scopeStack.pushProps(vars, currentScope().node);
}
}
}
else if (matchImport("createResource", callee.name)) {
const resourceReturn = id && getNthDestructuredVar(id, 0, context.getScope());
if (resourceReturn) {
scopeStack.pushProps(resourceReturn, currentScope().node);
}
}
else if (matchImport("createMutable", callee.name)) {
const mutable = id && getReturnedVar(id, context.getScope());
if (mutable) {
scopeStack.pushProps(mutable, currentScope().node);
}
}
else if (matchImport("mapArray", callee.name)) {
const arg1 = init.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1) &&
arg1.params.length >= 2 &&
arg1.params[1].type === "Identifier") {
const indexSignal = findVariable(context.getScope(), arg1.params[1]);
if (indexSignal) {
scopeStack.pushSignal(indexSignal);
}
}
}
else if (matchImport("indexArray", callee.name)) {
const arg1 = init.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1) &&
arg1.params.length >= 1 &&
arg1.params[0].type === "Identifier") {
const valueSignal = findVariable(context.getScope(), arg1.params[0]);
if (valueSignal) {
scopeStack.pushSignal(valueSignal);
}
}
}
}
};
const checkForTrackedScopes = (node) => {
const pushTrackedScope = (node, expect) => {
currentScope().trackedScopes.push({ node, expect });
if (expect !== "called-function" && (0, utils_2.isFunctionNode)(node) && node.async) {
context.report({
node,
messageId: "noAsyncTrackedScope",
});
}
};
const permissivelyTrackNode = (node) => {
(0, estraverse_1.traverse)(node, {
enter(cn) {
const childNode = cn;
const traced = (0, utils_2.trace)(childNode, context.getScope());
if ((0, utils_2.isFunctionNode)(traced) ||
(traced.type === "Identifier" &&
traced.parent.type !== "MemberExpression" &&
!(traced.parent.type === "CallExpression" && traced.parent.callee === traced))) {
pushTrackedScope(childNode, "called-function");
this.skip();
}
},
});
};
if (node.type === "JSXExpressionContainer") {
if (node.parent?.type === "JSXAttribute" &&
/^on[:A-Z]/.test(sourceCode.getText(node.parent.name)) &&
node.parent.parent?.type === "JSXOpeningElement" &&
node.parent.parent.name.type === "JSXIdentifier" &&
(0, utils_2.isDOMElementName)(node.parent.parent.name.name)) {
pushTrackedScope(node.expression, "called-function");
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.type === "JSXNamespacedName" &&
node.parent.name.namespace.name === "use" &&
(0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "called-function");
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.name === "value" &&
node.parent.parent?.type === "JSXOpeningElement" &&
((node.parent.parent.name.type === "JSXIdentifier" &&
node.parent.parent.name.name.endsWith("Provider")) ||
(node.parent.parent.name.type === "JSXMemberExpression" &&
node.parent.parent.name.property.name === "Provider"))) {
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name?.type === "JSXIdentifier" &&
/^static[A-Z]/.test(node.parent.name.name) &&
node.parent.parent?.type === "JSXOpeningElement" &&
node.parent.parent.name.type === "JSXIdentifier" &&
!(0, utils_2.isDOMElementName)(node.parent.parent.name.name)) {
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.name === "ref" &&
(0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "called-function");
}
else if ((0, utils_2.isJSXElementOrFragment)(node.parent) && (0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "function");
}
else {
pushTrackedScope(node.expression, "expression");
}
}
else if (node.type === "JSXSpreadAttribute") {
pushTrackedScope(node.argument, "expression");
}
else if (node.type === "NewExpression") {
const { callee, arguments: { 0: arg0 }, } = node;
if (callee.type === "Identifier" &&
arg0 &&
[
"IntersectionObserver",
"MutationObserver",
"PerformanceObserver",
"ReportingObserver",
"ResizeObserver",
].includes(callee.name)) {
pushTrackedScope(arg0, "called-function");
}
}
else if (node.type === "CallExpression") {
if (node.callee.type === "Identifier") {
const { callee, arguments: { 0: arg0, 1: arg1 }, } = node;
if (matchImport([
"createMemo",
"children",
"createEffect",
"createRenderEffect",
"createDeferred",
"createComputed",
"createSelector",
"untrack",
"mapArray",
"indexArray",
"observable",
], callee.name) ||
(matchImport("createResource", callee.name) && node.arguments.length >= 2)) {
pushTrackedScope(arg0, "function");
}
else if (matchImport(["onMount", "onCleanup", "onError"], callee.name) ||
[
"setInterval",
"setTimeout",
"setImmediate",
"requestAnimationFrame",
"requestIdleCallback",
].includes(callee.name)) {
pushTrackedScope(arg0, "called-function");
}
else if (matchImport("on", callee.name)) {
if (arg0) {
if (arg0.type === "ArrayExpression") {
arg0.elements.forEach((element) => {
if (element && element?.type !== "SpreadElement") {
pushTrackedScope(element, "function");
}
});
}
else {
pushTrackedScope(arg0, "function");
}
}
if (arg1) {
pushTrackedScope(arg1, "called-function");
}
}
else if (matchImport("createStore", callee.name) && arg0?.type === "ObjectExpression") {
for (const property of arg0.properties) {
if (property.type === "Property" &&
property.kind === "get" &&
(0, utils_2.isFunctionNode)(property.value)) {
pushTrackedScope(property.value, "function");
}
}
}
else if (matchImport("runWithOwner", callee.name)) {
if (arg1) {
let isTrackedScope = true;
const owner = arg0.type === "Identifier" && findVariable(context.getScope(), arg0);
if (owner) {
const decl = owner.defs[0];
if (decl &&
decl.node.type === "VariableDeclarator" &&
decl.node.init?.type === "CallExpression" &&
decl.node.init.callee.type === "Identifier" &&
matchImport("getOwner", decl.node.init.callee.name)) {
const ownerFunction = (0, utils_2.findParent)(decl.node, utils_2.isProgramOrFunctionNode);
const scopeStackIndex = scopeStack.findIndex(({ node }) => ownerFunction === node);
if ((scopeStackIndex >= 1 &&
!scopeStack[scopeStackIndex - 1].trackedScopes.some((trackedScope) => trackedScope.expect === "function" && trackedScope.node === ownerFunction)) ||
scopeStackIndex === 0) {
isTrackedScope = false;
}
}
}
if (isTrackedScope) {
pushTrackedScope(arg1, "function");
}
}
}
else if (/^(?:use|create)[A-Z]/.test(callee.name) ||
options.customReactiveFunctions.includes(callee.name)) {
for (const arg of node.arguments) {
permissivelyTrackNode(arg);
}
}
}
else if (node.callee.type === "MemberExpression") {
const { property } = node.callee;
if (property.type === "Identifier" &&
property.name === "addEventListener" &&
node.arguments.length >= 2) {
pushTrackedScope(node.arguments[1], "called-function");
}
else if (property.type === "Identifier" &&
(/^(?:use|create)[A-Z]/.test(property.name) ||
options.customReactiveFunctions.includes(property.name))) {
for (const arg of node.arguments) {
permissivelyTrackNode(arg);
}
}
}
}
else if (node.type === "VariableDeclarator") {
if (node.init?.type === "CallExpression" && node.init.callee.type === "Identifier") {
if (matchImport(["createReactive", "createReaction"], node.init.callee.name)) {
const track = getReturnedVar(node.id, context.getScope());
if (track) {
for (const reference of track.references) {
if (!reference.init &&
reference.isReadOnly() &&
reference.identifier.parent?.type === "CallExpression" &&
reference.identifier.parent.callee === reference.identifier) {
const arg0 = reference.identifier.parent.arguments[0];
arg0 && pushTrackedScope(arg0, "function");
}
}
}
if ((0, utils_2.isFunctionNode)(node.init.arguments[0])) {
pushTrackedScope(node.init.arguments[0], "called-function");
}
}
}
}
else if (node.type === "AssignmentExpression") {
if (node.left.type === "MemberExpression" &&
node.left.property.type === "Identifier" &&
(0, utils_2.isFunctionNode)(node.right) &&
/^on[a-z]+$/.test(node.left.property.name)) {
pushTrackedScope(node.right, "called-function");
}
}
else if (node.type === "TaggedTemplateExpression") {
for (const expression of node.quasi.expressions) {
if ((0, utils_2.isFunctionNode)(expression)) {
pushTrackedScope(expression, "called-function");
for (const param of expression.params) {
if (param.type === "Identifier" && (0, utils_2.isPropsByName)(param.name)) {
const variable = findVariable(context.getScope(), param);
if (variable)
scopeStack.pushProps(variable, currentScope().node);
}
}
}
}
}
};
return {
ImportDeclaration: handleImportDeclaration,
JSXExpressionContainer(node) {
checkForTrackedScopes(node);
},
JSXSpreadAttribute(node) {
checkForTrackedScopes(node);
},
CallExpression(node) {
checkForTrackedScopes(node);
checkForSyncCallbacks(node);
const parent = node.parent && (0, utils_2.ignoreTransparentWrappers)(node.parent, true);
if (parent?.type !== "AssignmentExpression" && parent?.type !== "VariableDeclarator") {
checkForReactiveAssignment(null, node);
}
},
NewExpression(node) {
checkForTrackedScopes(node);
},
VariableDeclarator(node) {
if (node.init) {
checkForReactiveAssignment(node.id, node.init);
checkForTrackedScopes(node);
}
},
AssignmentExpression(node) {
if (node.left.type !== "MemberExpression") {
checkForReactiveAssignment(node.left, node.right);
}
checkForTrackedScopes(node);
},
TaggedTemplateExpression(node) {
checkForTrackedScopes(node);
},
"JSXElement > JSXExpressionContainer > :function"(node) {
if ((0, utils_2.isFunctionNode)(node) &&
node.parent?.type === "JSXExpressionContainer" &&
node.parent.parent?.type === "JSXElement") {
const element = node.parent.parent;
if (element.openingElement.name.type === "JSXIdentifier") {
const tagName = element.openingElement.name.name;
if (matchImport("For", tagName) &&
node.params.length === 2 &&
node.params[1].type === "Identifier") {
const index = findVariable(context.getScope(), node.params[1]);
if (index) {
scopeStack.pushSignal(index, currentScope().node);
}
}
else if (matchImport("Index", tagName) &&
node.params.length >= 1 &&
node.params[0].type === "Identifier") {
const item = findVariable(context.getScope(), node.params[0]);
if (item) {
scopeStack.pushSignal(item, currentScope().node);
}
}
}
}
},
FunctionExpression: onFunctionEnter,
ArrowFunctionExpression: onFunctionEnter,
FunctionDeclaration: onFunctionEnter,
Program: onFunctionEnter,
"FunctionExpression:exit": onFunctionExit,
"ArrowFunctionExpression:exit": onFunctionExit,
"FunctionDeclaration:exit": onFunctionExit,
"Program:exit": onFunctionExit,
JSXElement() {
if (scopeStack.length) {
currentScope().hasJSX = true;
}
},
JSXFragment() {
if (scopeStack.length) {
currentScope().hasJSX = true;
}
},
};
},
});

View File

@@ -0,0 +1,8 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "selfClose" | "dontSelfClose";
type Options = [{
component?: "all" | "none";
html?: "all" | "void" | "none";
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
function isComponent(node) {
return ((node.name.type === "JSXIdentifier" && !(0, utils_2.isDOMElementName)(node.name.name)) ||
node.name.type === "JSXMemberExpression");
}
const voidDOMElementRegex = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
function isVoidDOMElementName(name) {
return voidDOMElementRegex.test(name);
}
function childrenIsEmpty(node) {
return node.parent.children.length === 0;
}
function childrenIsMultilineSpaces(node) {
const childrens = node.parent.children;
return (childrens.length === 1 &&
childrens[0].type === "JSXText" &&
childrens[0].value.indexOf("\n") !== -1 &&
childrens[0].value.replace(/(?!\xA0)\s/g, "") === "");
}
exports.default = createRule({
meta: {
type: "layout",
docs: {
description: "Disallow extra closing tags for components without children.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/self-closing-comp.md",
},
fixable: "code",
schema: [
{
type: "object",
properties: {
component: {
type: "string",
description: "which Solid components should be self-closing when possible",
enum: ["all", "none"],
default: "all",
},
html: {
type: "string",
description: "which native elements should be self-closing when possible",
enum: ["all", "void", "none"],
default: "all",
},
},
additionalProperties: false,
},
],
messages: {
selfClose: "Empty components are self-closing.",
dontSelfClose: "This element should not be self-closing.",
},
},
defaultOptions: [],
create(context) {
function shouldBeSelfClosedWhenPossible(node) {
if (isComponent(node)) {
const whichComponents = context.options[0]?.component ?? "all";
return whichComponents === "all";
}
else if (node.name.type === "JSXIdentifier" && (0, utils_2.isDOMElementName)(node.name.name)) {
const whichComponents = context.options[0]?.html ?? "all";
switch (whichComponents) {
case "all":
return true;
case "void":
return isVoidDOMElementName(node.name.name);
case "none":
return false;
}
}
return true;
}
return {
JSXOpeningElement(node) {
const canSelfClose = childrenIsEmpty(node) || childrenIsMultilineSpaces(node);
if (canSelfClose) {
const shouldSelfClose = shouldBeSelfClosedWhenPossible(node);
if (shouldSelfClose && !node.selfClosing) {
context.report({
node,
messageId: "selfClose",
fix(fixer) {
const openingElementEnding = node.range[1] - 1;
const closingElementEnding = node.parent.closingElement.range[1];
const range = [openingElementEnding, closingElementEnding];
return fixer.replaceTextRange(range, " />");
},
});
}
else if (!shouldSelfClose && node.selfClosing) {
context.report({
node,
messageId: "dontSelfClose",
fix(fixer) {
const sourceCode = context.getSourceCode();
const tagName = context.getSourceCode().getText(node.name);
const selfCloseEnding = node.range[1];
const lastTokens = sourceCode.getLastTokens(node, { count: 3 });
const isSpaceBeforeSelfClose = sourceCode.isSpaceBetween?.(lastTokens[0], lastTokens[1]);
const range = [
isSpaceBeforeSelfClose ? selfCloseEnding - 3 : selfCloseEnding - 2,
selfCloseEnding,
];
return fixer.replaceTextRange(range, `></${tagName}>`);
},
});
}
}
},
};
},
});

View File

@@ -0,0 +1,8 @@
import type { TSESLint } from "@typescript-eslint/utils";
type MessageIds = "kebabStyleProp" | "invalidStyleProp" | "numericStyleValue" | "stringStyle";
type Options = [{
styleProps?: Array<string>;
allowString?: boolean;
}?];
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
export default _default;

View File

@@ -0,0 +1,119 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const kebab_case_1 = __importDefault(require("kebab-case"));
const known_css_properties_1 = require("known-css-properties");
const style_to_object_1 = __importDefault(require("style-to-object"));
const utils_2 = require("../utils");
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
const { getPropertyName, getStaticValue } = utils_1.ASTUtils;
const lengthPercentageRegex = /\b(?:width|height|margin|padding|border-width|font-size)\b/i;
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Require CSS properties in the `style` prop to be valid and kebab-cased (ex. 'font-size'), not camel-cased (ex. 'fontSize') like in React, " +
"and that property values with dimensions are strings, not numbers with implicit 'px' units.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/style-prop.md",
},
fixable: "code",
schema: [
{
type: "object",
properties: {
styleProps: {
description: "an array of prop names to treat as a CSS style object",
default: ["style"],
type: "array",
items: {
type: "string",
},
minItems: 1,
uniqueItems: true,
},
allowString: {
description: "if allowString is set to true, this rule will not convert a style string literal into a style object (not recommended for performance)",
type: "boolean",
default: false,
},
},
additionalProperties: false,
},
],
messages: {
kebabStyleProp: "Use {{ kebabName }} instead of {{ name }}.",
invalidStyleProp: "{{ name }} is not a valid CSS property.",
numericStyleValue: 'This CSS property value should be a string with a unit; Solid does not automatically append a "px" unit.',
stringStyle: "Use an object for the style prop instead of a string.",
},
},
defaultOptions: [],
create(context) {
const allCssPropertiesSet = new Set(known_css_properties_1.all);
const allowString = Boolean(context.options[0]?.allowString);
const styleProps = context.options[0]?.styleProps || ["style"];
return {
JSXAttribute(node) {
if (styleProps.indexOf((0, utils_2.jsxPropName)(node)) === -1) {
return;
}
const style = node.value?.type === "JSXExpressionContainer" ? node.value.expression : node.value;
if (!style) {
return;
}
else if (style.type === "Literal" && typeof style.value === "string" && !allowString) {
let objectStyles;
try {
objectStyles = (0, style_to_object_1.default)(style.value) ?? undefined;
}
catch (e) { }
context.report({
node: style,
messageId: "stringStyle",
fix: objectStyles &&
((fixer) => fixer.replaceText(node.value, `{${JSON.stringify(objectStyles)}}`)),
});
}
else if (style.type === "TemplateLiteral" && !allowString) {
context.report({
node: style,
messageId: "stringStyle",
});
}
else if (style.type === "ObjectExpression") {
const properties = style.properties.filter((prop) => prop.type === "Property");
properties.forEach((prop) => {
const name = getPropertyName(prop, context.getScope());
if (name && !name.startsWith("--") && !allCssPropertiesSet.has(name)) {
const kebabName = (0, kebab_case_1.default)(name);
if (allCssPropertiesSet.has(kebabName)) {
context.report({
node: prop.key,
messageId: "kebabStyleProp",
data: { name, kebabName },
fix: (fixer) => fixer.replaceText(prop.key, `"${kebabName}"`),
});
}
else {
context.report({
node: prop.key,
messageId: "invalidStyleProp",
data: { name },
});
}
}
else if (!name || (!name.startsWith("--") && lengthPercentageRegex.test(name))) {
const value = getStaticValue(prop.value)?.value;
if (typeof value === "number" && value !== 0) {
context.report({ node: prop.value, messageId: "numericStyleValue" });
}
}
});
}
},
};
},
});

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

31
node_modules/eslint-plugin-solid/dist/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import { TSESTree as T, TSESLint } from "@typescript-eslint/utils";
export declare const isDOMElementName: (name: string) => boolean;
export declare const isPropsByName: (name: string) => boolean;
export declare const formatList: (strings: Array<string>) => string;
export declare const find: (node: T.Node, predicate: (node: T.Node) => boolean) => T.Node | null;
export declare function findParent<Guard extends T.Node>(node: T.Node, predicate: (node: T.Node) => node is Guard): Guard | null;
export declare function findParent(node: T.Node, predicate: (node: T.Node) => boolean): T.Node | null;
export declare function trace(node: T.Node, initialScope: TSESLint.Scope.Scope): T.Node;
export declare function ignoreTransparentWrappers(node: T.Node, up?: boolean): T.Node;
export type FunctionNode = T.FunctionExpression | T.ArrowFunctionExpression | T.FunctionDeclaration;
export declare const isFunctionNode: (node: T.Node | null | undefined) => node is FunctionNode;
export type ProgramOrFunctionNode = FunctionNode | T.Program;
export declare const isProgramOrFunctionNode: (node: T.Node | null | undefined) => node is ProgramOrFunctionNode;
export declare const isJSXElementOrFragment: (node: T.Node | null | undefined) => node is T.JSXElement | T.JSXFragment;
export declare const getFunctionName: (node: FunctionNode) => string | null;
export declare function findInScope(node: T.Node, scope: ProgramOrFunctionNode, predicate: (node: T.Node) => boolean): T.Node | null;
export declare const getCommentBefore: (node: T.Node, sourceCode: TSESLint.SourceCode) => T.Comment | undefined;
export declare const getCommentAfter: (node: T.Node, sourceCode: TSESLint.SourceCode) => T.Comment | undefined;
export declare const trackImports: (fromModule?: RegExp) => {
matchImport: (imports: string | Array<string>, str: string) => string | undefined;
handleImportDeclaration: (node: T.ImportDeclaration) => void;
};
export declare function appendImports(fixer: TSESLint.RuleFixer, sourceCode: TSESLint.SourceCode, importNode: T.ImportDeclaration, identifiers: Array<string>): TSESLint.RuleFix | null;
export declare function insertImports(fixer: TSESLint.RuleFixer, sourceCode: TSESLint.SourceCode, source: string, identifiers: Array<string>, aboveImport?: T.ImportDeclaration, isType?: boolean): TSESLint.RuleFix;
export declare function removeSpecifier(fixer: TSESLint.RuleFixer, sourceCode: TSESLint.SourceCode, specifier: T.ImportSpecifier, pure?: boolean): TSESLint.RuleFix;
export declare function jsxPropName(prop: T.JSXAttribute): string;
type Props = T.JSXOpeningElement["attributes"];
export declare function jsxGetAllProps(props: Props): Generator<[string, T.Node]>;
export declare function jsxHasProp(props: Props, prop: string): boolean;
export declare function jsxGetProp(props: Props, prop: string): T.JSXAttribute | undefined;
export {};

217
node_modules/eslint-plugin-solid/dist/utils.js generated vendored Normal file
View File

@@ -0,0 +1,217 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsxGetProp = exports.jsxHasProp = exports.jsxGetAllProps = exports.jsxPropName = exports.removeSpecifier = exports.insertImports = exports.appendImports = exports.trackImports = exports.getCommentAfter = exports.getCommentBefore = exports.findInScope = exports.getFunctionName = exports.isJSXElementOrFragment = exports.isProgramOrFunctionNode = exports.isFunctionNode = exports.ignoreTransparentWrappers = exports.trace = exports.findParent = exports.find = exports.formatList = exports.isPropsByName = exports.isDOMElementName = void 0;
const utils_1 = require("@typescript-eslint/utils");
const { findVariable } = utils_1.ASTUtils;
const domElementRegex = /^[a-z]/;
const isDOMElementName = (name) => domElementRegex.test(name);
exports.isDOMElementName = isDOMElementName;
const propsRegex = /[pP]rops/;
const isPropsByName = (name) => propsRegex.test(name);
exports.isPropsByName = isPropsByName;
const formatList = (strings) => {
if (strings.length === 0) {
return "";
}
else if (strings.length === 1) {
return `'${strings[0]}'`;
}
else if (strings.length === 2) {
return `'${strings[0]}' and '${strings[1]}'`;
}
else {
const last = strings.length - 1;
return `${strings
.slice(0, last)
.map((s) => `'${s}'`)
.join(", ")}, and '${strings[last]}'`;
}
};
exports.formatList = formatList;
const find = (node, predicate) => {
let n = node;
while (n) {
const result = predicate(n);
if (result) {
return n;
}
n = n.parent;
}
return null;
};
exports.find = find;
function findParent(node, predicate) {
return node.parent ? (0, exports.find)(node.parent, predicate) : null;
}
exports.findParent = findParent;
function trace(node, initialScope) {
if (node.type === "Identifier") {
const variable = findVariable(initialScope, node);
if (!variable)
return node;
const def = variable.defs[0];
switch (def?.type) {
case "FunctionName":
case "ClassName":
case "ImportBinding":
return def.node;
case "Variable":
if ((def.node.parent.kind === "const" ||
variable.references.every((ref) => ref.init || ref.isReadOnly())) &&
def.node.id.type === "Identifier" &&
def.node.init) {
return trace(def.node.init, initialScope);
}
}
}
return node;
}
exports.trace = trace;
function ignoreTransparentWrappers(node, up = false) {
if (node.type === "TSAsExpression" ||
node.type === "TSNonNullExpression" ||
node.type === "TSSatisfiesExpression") {
const next = up ? node.parent : node.expression;
if (next) {
return ignoreTransparentWrappers(next, up);
}
}
return node;
}
exports.ignoreTransparentWrappers = ignoreTransparentWrappers;
const FUNCTION_TYPES = ["FunctionExpression", "ArrowFunctionExpression", "FunctionDeclaration"];
const isFunctionNode = (node) => !!node && FUNCTION_TYPES.includes(node.type);
exports.isFunctionNode = isFunctionNode;
const PROGRAM_OR_FUNCTION_TYPES = ["Program"].concat(FUNCTION_TYPES);
const isProgramOrFunctionNode = (node) => !!node && PROGRAM_OR_FUNCTION_TYPES.includes(node.type);
exports.isProgramOrFunctionNode = isProgramOrFunctionNode;
const isJSXElementOrFragment = (node) => node?.type === "JSXElement" || node?.type === "JSXFragment";
exports.isJSXElementOrFragment = isJSXElementOrFragment;
const getFunctionName = (node) => {
if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression") &&
node.id != null) {
return node.id.name;
}
if (node.parent?.type === "VariableDeclarator" && node.parent.id.type === "Identifier") {
return node.parent.id.name;
}
return null;
};
exports.getFunctionName = getFunctionName;
function findInScope(node, scope, predicate) {
const found = (0, exports.find)(node, (node) => node === scope || predicate(node));
return found === scope && !predicate(node) ? null : found;
}
exports.findInScope = findInScope;
const getCommentBefore = (node, sourceCode) => sourceCode
.getCommentsBefore(node)
.find((comment) => comment.loc.end.line >= node.loc.start.line - 1);
exports.getCommentBefore = getCommentBefore;
const getCommentAfter = (node, sourceCode) => sourceCode
.getCommentsAfter(node)
.find((comment) => comment.loc.start.line === node.loc.end.line);
exports.getCommentAfter = getCommentAfter;
const trackImports = (fromModule = /^solid-js(?:\/?|\b)/) => {
const importMap = new Map();
const handleImportDeclaration = (node) => {
if (fromModule.test(node.source.value)) {
for (const specifier of node.specifiers) {
if (specifier.type === "ImportSpecifier") {
importMap.set(specifier.imported.name, specifier.local.name);
}
}
}
};
const matchImport = (imports, str) => {
const importArr = Array.isArray(imports) ? imports : [imports];
return importArr.find((i) => importMap.get(i) === str);
};
return { matchImport, handleImportDeclaration };
};
exports.trackImports = trackImports;
function appendImports(fixer, sourceCode, importNode, identifiers) {
const identifiersString = identifiers.join(", ");
const reversedSpecifiers = importNode.specifiers.slice().reverse();
const lastSpecifier = reversedSpecifiers.find((s) => s.type === "ImportSpecifier");
if (lastSpecifier) {
return fixer.insertTextAfter(lastSpecifier, `, ${identifiersString}`);
}
const otherSpecifier = importNode.specifiers.find((s) => s.type === "ImportDefaultSpecifier" || s.type === "ImportNamespaceSpecifier");
if (otherSpecifier) {
return fixer.insertTextAfter(otherSpecifier, `, { ${identifiersString} }`);
}
if (importNode.specifiers.length === 0) {
const [importToken, maybeBrace] = sourceCode.getFirstTokens(importNode, { count: 2 });
if (maybeBrace?.value === "{") {
return fixer.insertTextAfter(maybeBrace, ` ${identifiersString} `);
}
else {
return importToken
? fixer.insertTextAfter(importToken, ` { ${identifiersString} } from`)
: null;
}
}
return null;
}
exports.appendImports = appendImports;
function insertImports(fixer, sourceCode, source, identifiers, aboveImport, isType = false) {
const identifiersString = identifiers.join(", ");
const programNode = sourceCode.ast;
const firstImport = aboveImport ?? programNode.body.find((n) => n.type === "ImportDeclaration");
if (firstImport) {
return fixer.insertTextBeforeRange(((0, exports.getCommentBefore)(firstImport, sourceCode) ?? firstImport).range, `import ${isType ? "type " : ""}{ ${identifiersString} } from "${source}";\n`);
}
return fixer.insertTextBeforeRange([0, 0], `import ${isType ? "type " : ""}{ ${identifiersString} } from "${source}";\n`);
}
exports.insertImports = insertImports;
function removeSpecifier(fixer, sourceCode, specifier, pure = true) {
const declaration = specifier.parent;
if (declaration.specifiers.length === 1 && pure) {
return fixer.remove(declaration);
}
const maybeComma = sourceCode.getTokenAfter(specifier);
if (maybeComma?.value === ",") {
return fixer.removeRange([specifier.range[0], maybeComma.range[1]]);
}
return fixer.remove(specifier);
}
exports.removeSpecifier = removeSpecifier;
function jsxPropName(prop) {
if (prop.name.type === "JSXNamespacedName") {
return `${prop.name.namespace.name}:${prop.name.name.name}`;
}
return prop.name.name;
}
exports.jsxPropName = jsxPropName;
function* jsxGetAllProps(props) {
for (const attr of props) {
if (attr.type === "JSXSpreadAttribute" && attr.argument.type === "ObjectExpression") {
for (const property of attr.argument.properties) {
if (property.type === "Property") {
if (property.key.type === "Identifier") {
yield [property.key.name, property.key];
}
else if (property.key.type === "Literal") {
yield [String(property.key.value), property.key];
}
}
}
}
else if (attr.type === "JSXAttribute") {
yield [jsxPropName(attr), attr.name];
}
}
}
exports.jsxGetAllProps = jsxGetAllProps;
function jsxHasProp(props, prop) {
for (const [p] of jsxGetAllProps(props)) {
if (p === prop)
return true;
}
return false;
}
exports.jsxHasProp = jsxHasProp;
function jsxGetProp(props, prop) {
return props.find((attribute) => attribute.type !== "JSXSpreadAttribute" && prop === jsxPropName(attribute));
}
exports.jsxGetProp = jsxGetProp;

View File

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

View File

@@ -0,0 +1,10 @@
# `@typescript-eslint/scope-manager`
[![NPM Version](https://img.shields.io/npm/v/@typescript-eslint/scope-manager.svg?style=flat-square)](https://www.npmjs.com/package/@typescript-eslint/scope-manager)
[![NPM Downloads](https://img.shields.io/npm/dm/@typescript-eslint/scope-manager.svg?style=flat-square)](https://www.npmjs.com/package/@typescript-eslint/scope-manager)
👉 See **https://typescript-eslint.io/packages/scope-manager** for documentation on this package.
> See https://typescript-eslint.io for general documentation on typescript-eslint, the tooling that allows you to run ESLint and Prettier on TypeScript code.
<!-- Local path for docs: docs/packages/Scope_Manager.mdx -->

View File

@@ -0,0 +1,4 @@
declare function createIdGenerator(): () => number;
declare function resetIds(): void;
export { createIdGenerator, resetIds };
//# sourceMappingURL=ID.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ID.d.ts","sourceRoot":"","sources":["../src/ID.ts"],"names":[],"mappings":"AAGA,iBAAS,iBAAiB,IAAI,MAAM,MAAM,CAUzC;AAED,iBAAS,QAAQ,IAAI,IAAI,CAExB;AAED,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC"}

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resetIds = exports.createIdGenerator = void 0;
const ID_CACHE = new Map();
let NEXT_KEY = 0;
function createIdGenerator() {
const key = (NEXT_KEY += 1);
ID_CACHE.set(key, 0);
return () => {
const current = ID_CACHE.get(key) ?? 0;
const next = current + 1;
ID_CACHE.set(key, next);
return next;
};
}
exports.createIdGenerator = createIdGenerator;
function resetIds() {
ID_CACHE.clear();
}
exports.resetIds = resetIds;
//# sourceMappingURL=ID.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ID.js","sourceRoot":"","sources":["../src/ID.ts"],"names":[],"mappings":";;;AAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC3C,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,SAAS,iBAAiB;IACxB,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAErB,OAAO,GAAW,EAAE;QAClB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAMQ,8CAAiB;AAJ1B,SAAS,QAAQ;IACf,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAE2B,4BAAQ"}

View File

@@ -0,0 +1,72 @@
import type { SourceType, TSESTree } from '@typescript-eslint/types';
import type { Scope } from './scope';
import { BlockScope, CatchScope, ClassScope, ConditionalTypeScope, ForScope, FunctionExpressionNameScope, FunctionScope, FunctionTypeScope, GlobalScope, MappedTypeScope, ModuleScope, SwitchScope, TSEnumScope, TSModuleScope, TypeScope, WithScope } from './scope';
import { ClassFieldInitializerScope } from './scope/ClassFieldInitializerScope';
import { ClassStaticBlockScope } from './scope/ClassStaticBlockScope';
import type { Variable } from './variable';
interface ScopeManagerOptions {
globalReturn?: boolean;
sourceType?: SourceType;
impliedStrict?: boolean;
}
/**
* @see https://eslint.org/docs/latest/developer-guide/scope-manager-interface#scopemanager-interface
*/
declare class ScopeManager {
#private;
currentScope: Scope | null;
readonly declaredVariables: WeakMap<TSESTree.Node, Variable[]>;
/**
* The root scope
*/
globalScope: GlobalScope | null;
readonly nodeToScope: WeakMap<TSESTree.Node, Scope[]>;
/**
* All scopes
* @public
*/
readonly scopes: Scope[];
get variables(): Variable[];
constructor(options: ScopeManagerOptions);
isGlobalReturn(): boolean;
isModule(): boolean;
isImpliedStrict(): boolean;
isStrictModeSupported(): boolean;
isES6(): boolean;
/**
* Get the variables that a given AST node defines. The gotten variables' `def[].node`/`def[].parent` property is the node.
* If the node does not define any variable, this returns an empty array.
* @param node An AST node to get their variables.
*/
getDeclaredVariables(node: TSESTree.Node): Variable[];
/**
* Get the scope of a given AST node. The gotten scope's `block` property is the node.
* This method never returns `function-expression-name` scope. If the node does not have their scope, this returns `null`.
*
* @param node An AST node to get their scope.
* @param inner If the node has multiple scopes, this returns the outermost scope normally.
* If `inner` is `true` then this returns the innermost scope.
*/
acquire(node: TSESTree.Node, inner?: boolean): Scope | null;
protected nestScope<T extends Scope>(scope: T): T;
nestBlockScope(node: BlockScope['block']): BlockScope;
nestCatchScope(node: CatchScope['block']): CatchScope;
nestClassScope(node: ClassScope['block']): ClassScope;
nestClassFieldInitializerScope(node: ClassFieldInitializerScope['block']): ClassFieldInitializerScope;
nestClassStaticBlockScope(node: ClassStaticBlockScope['block']): ClassStaticBlockScope;
nestConditionalTypeScope(node: ConditionalTypeScope['block']): ConditionalTypeScope;
nestForScope(node: ForScope['block']): ForScope;
nestFunctionExpressionNameScope(node: FunctionExpressionNameScope['block']): FunctionExpressionNameScope;
nestFunctionScope(node: FunctionScope['block'], isMethodDefinition: boolean): FunctionScope;
nestFunctionTypeScope(node: FunctionTypeScope['block']): FunctionTypeScope;
nestGlobalScope(node: GlobalScope['block']): GlobalScope;
nestMappedTypeScope(node: MappedTypeScope['block']): MappedTypeScope;
nestModuleScope(node: ModuleScope['block']): ModuleScope;
nestSwitchScope(node: SwitchScope['block']): SwitchScope;
nestTSEnumScope(node: TSEnumScope['block']): TSEnumScope;
nestTSModuleScope(node: TSModuleScope['block']): TSModuleScope;
nestTypeScope(node: TypeScope['block']): TypeScope;
nestWithScope(node: WithScope['block']): WithScope;
}
export { ScopeManager };
//# sourceMappingURL=ScopeManager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ScopeManager.d.ts","sourceRoot":"","sources":["../src/ScopeManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGrE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,QAAQ,EACR,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,WAAW,EAEX,WAAW,EACX,WAAW,EACX,aAAa,EACb,SAAS,EACT,SAAS,EACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,UAAU,mBAAmB;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,cAAM,YAAY;;IACT,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;IAClC,SAAgB,iBAAiB,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtE;;OAEG;IACI,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC,SAAgB,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAE7D;;;OAGG;IACH,SAAgB,MAAM,EAAE,KAAK,EAAE,CAAC;IAEhC,IAAW,SAAS,IAAI,QAAQ,EAAE,CAQjC;gBAEW,OAAO,EAAE,mBAAmB;IASjC,cAAc,IAAI,OAAO;IAIzB,QAAQ,IAAI,OAAO;IAInB,eAAe,IAAI,OAAO;IAI1B,qBAAqB,IAAI,OAAO;IAIhC,KAAK,IAAI,OAAO;IAIvB;;;;OAIG;IACI,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,GAAG,QAAQ,EAAE;IAI5D;;;;;;;OAOG;IACI,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,UAAQ,GAAG,KAAK,GAAG,IAAI;IAoChE,SAAS,CAAC,SAAS,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAU1C,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU;IAKrD,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU;IAKrD,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU;IAKrD,8BAA8B,CACnC,IAAI,EAAE,0BAA0B,CAAC,OAAO,CAAC,GACxC,0BAA0B;IAOtB,yBAAyB,CAC9B,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACnC,qBAAqB;IAOjB,wBAAwB,CAC7B,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAClC,oBAAoB;IAOhB,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,QAAQ;IAK/C,+BAA+B,CACpC,IAAI,EAAE,2BAA2B,CAAC,OAAO,CAAC,GACzC,2BAA2B;IAOvB,iBAAiB,CACtB,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,EAC5B,kBAAkB,EAAE,OAAO,GAC1B,aAAa;IAOT,qBAAqB,CAC1B,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC/B,iBAAiB;IAKb,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW;IAIxD,mBAAmB,CAAC,IAAI,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,eAAe;IAKpE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW;IAKxD,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW;IAKxD,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW;IAKxD,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,aAAa;IAK9D,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS;IAKlD,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS;CAI1D;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}

View File

@@ -0,0 +1,183 @@
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ScopeManager_options;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScopeManager = void 0;
const assert_1 = require("./assert");
const scope_1 = require("./scope");
const ClassFieldInitializerScope_1 = require("./scope/ClassFieldInitializerScope");
const ClassStaticBlockScope_1 = require("./scope/ClassStaticBlockScope");
/**
* @see https://eslint.org/docs/latest/developer-guide/scope-manager-interface#scopemanager-interface
*/
class ScopeManager {
get variables() {
const variables = new Set();
function recurse(scope) {
scope.variables.forEach(v => variables.add(v));
scope.childScopes.forEach(recurse);
}
this.scopes.forEach(recurse);
return Array.from(variables).sort((a, b) => a.$id - b.$id);
}
constructor(options) {
_ScopeManager_options.set(this, void 0);
this.scopes = [];
this.globalScope = null;
this.nodeToScope = new WeakMap();
this.currentScope = null;
__classPrivateFieldSet(this, _ScopeManager_options, options, "f");
this.declaredVariables = new WeakMap();
}
isGlobalReturn() {
return __classPrivateFieldGet(this, _ScopeManager_options, "f").globalReturn === true;
}
isModule() {
return __classPrivateFieldGet(this, _ScopeManager_options, "f").sourceType === 'module';
}
isImpliedStrict() {
return __classPrivateFieldGet(this, _ScopeManager_options, "f").impliedStrict === true;
}
isStrictModeSupported() {
return true;
}
isES6() {
return true;
}
/**
* Get the variables that a given AST node defines. The gotten variables' `def[].node`/`def[].parent` property is the node.
* If the node does not define any variable, this returns an empty array.
* @param node An AST node to get their variables.
*/
getDeclaredVariables(node) {
return this.declaredVariables.get(node) ?? [];
}
/**
* Get the scope of a given AST node. The gotten scope's `block` property is the node.
* This method never returns `function-expression-name` scope. If the node does not have their scope, this returns `null`.
*
* @param node An AST node to get their scope.
* @param inner If the node has multiple scopes, this returns the outermost scope normally.
* If `inner` is `true` then this returns the innermost scope.
*/
acquire(node, inner = false) {
function predicate(testScope) {
if (testScope.type === scope_1.ScopeType.function &&
testScope.functionExpressionScope) {
return false;
}
return true;
}
const scopes = this.nodeToScope.get(node);
if (!scopes || scopes.length === 0) {
return null;
}
// Heuristic selection from all scopes.
// If you would like to get all scopes, please use ScopeManager#acquireAll.
if (scopes.length === 1) {
return scopes[0];
}
if (inner) {
for (let i = scopes.length - 1; i >= 0; --i) {
const scope = scopes[i];
if (predicate(scope)) {
return scope;
}
}
return null;
}
return scopes.find(predicate) ?? null;
}
nestScope(scope) {
if (scope instanceof scope_1.GlobalScope) {
(0, assert_1.assert)(this.currentScope == null);
this.globalScope = scope;
}
this.currentScope = scope;
return scope;
}
nestBlockScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.BlockScope(this, this.currentScope, node));
}
nestCatchScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.CatchScope(this, this.currentScope, node));
}
nestClassScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.ClassScope(this, this.currentScope, node));
}
nestClassFieldInitializerScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new ClassFieldInitializerScope_1.ClassFieldInitializerScope(this, this.currentScope, node));
}
nestClassStaticBlockScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new ClassStaticBlockScope_1.ClassStaticBlockScope(this, this.currentScope, node));
}
nestConditionalTypeScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.ConditionalTypeScope(this, this.currentScope, node));
}
nestForScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.ForScope(this, this.currentScope, node));
}
nestFunctionExpressionNameScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.FunctionExpressionNameScope(this, this.currentScope, node));
}
nestFunctionScope(node, isMethodDefinition) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.FunctionScope(this, this.currentScope, node, isMethodDefinition));
}
nestFunctionTypeScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.FunctionTypeScope(this, this.currentScope, node));
}
nestGlobalScope(node) {
return this.nestScope(new scope_1.GlobalScope(this, node));
}
nestMappedTypeScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.MappedTypeScope(this, this.currentScope, node));
}
nestModuleScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.ModuleScope(this, this.currentScope, node));
}
nestSwitchScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.SwitchScope(this, this.currentScope, node));
}
nestTSEnumScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.TSEnumScope(this, this.currentScope, node));
}
nestTSModuleScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.TSModuleScope(this, this.currentScope, node));
}
nestTypeScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.TypeScope(this, this.currentScope, node));
}
nestWithScope(node) {
(0, assert_1.assert)(this.currentScope);
return this.nestScope(new scope_1.WithScope(this, this.currentScope, node));
}
}
exports.ScopeManager = ScopeManager;
_ScopeManager_options = new WeakMap();
//# sourceMappingURL=ScopeManager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,56 @@
import type { Lib, SourceType, TSESTree } from '@typescript-eslint/types';
import type { ReferencerOptions } from './referencer';
import { ScopeManager } from './ScopeManager';
interface AnalyzeOptions {
/**
* Known visitor keys.
*/
childVisitorKeys?: ReferencerOptions['childVisitorKeys'];
/**
* Whether the whole script is executed under node.js environment.
* When enabled, the scope manager adds a function scope immediately following the global scope.
* Defaults to `false`.
*/
globalReturn?: boolean;
/**
* Implied strict mode.
* Defaults to `false`.
*/
impliedStrict?: boolean;
/**
* The identifier that's used for JSX Element creation (after transpilation).
* This should not be a member expression - just the root identifier (i.e. use "React" instead of "React.createElement").
* Defaults to `"React"`.
*/
jsxPragma?: string | null;
/**
* The identifier that's used for JSX fragment elements (after transpilation).
* If `null`, assumes transpilation will always use a member on `jsxFactory` (i.e. React.Fragment).
* This should not be a member expression - just the root identifier (i.e. use "h" instead of "h.Fragment").
* Defaults to `null`.
*/
jsxFragmentName?: string | null;
/**
* The lib used by the project.
* This automatically defines a type variable for any types provided by the configured TS libs.
* Defaults to ['esnext'].
*
* https://www.typescriptlang.org/tsconfig#lib
*/
lib?: Lib[];
/**
* The source type of the script.
*/
sourceType?: SourceType;
/**
* Emit design-type metadata for decorated declarations in source.
* Defaults to `false`.
*/
emitDecoratorMetadata?: boolean;
}
/**
* Takes an AST and returns the analyzed scopes.
*/
declare function analyze(tree: TSESTree.Node, providedOptions?: AnalyzeOptions): ScopeManager;
export { analyze, AnalyzeOptions };
//# sourceMappingURL=analyze.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAM9C,UAAU,cAAc;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;IAEzD;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAEZ;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAaD;;GAEG;AACH,iBAAS,OAAO,CACd,IAAI,EAAE,QAAQ,CAAC,IAAI,EACnB,eAAe,CAAC,EAAE,cAAc,GAC/B,YAAY,CA6Bd;AAED,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC"}

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyze = void 0;
const visitor_keys_1 = require("@typescript-eslint/visitor-keys");
const referencer_1 = require("./referencer");
const ScopeManager_1 = require("./ScopeManager");
const DEFAULT_OPTIONS = {
childVisitorKeys: visitor_keys_1.visitorKeys,
globalReturn: false,
impliedStrict: false,
jsxPragma: 'React',
jsxFragmentName: null,
lib: ['es2018'],
sourceType: 'script',
emitDecoratorMetadata: false,
};
/**
* Takes an AST and returns the analyzed scopes.
*/
function analyze(tree, providedOptions) {
const options = {
childVisitorKeys: providedOptions?.childVisitorKeys ?? DEFAULT_OPTIONS.childVisitorKeys,
globalReturn: providedOptions?.globalReturn ?? DEFAULT_OPTIONS.globalReturn,
impliedStrict: providedOptions?.impliedStrict ?? DEFAULT_OPTIONS.impliedStrict,
jsxPragma: providedOptions?.jsxPragma === undefined
? DEFAULT_OPTIONS.jsxPragma
: providedOptions.jsxPragma,
jsxFragmentName: providedOptions?.jsxFragmentName ?? DEFAULT_OPTIONS.jsxFragmentName,
sourceType: providedOptions?.sourceType ?? DEFAULT_OPTIONS.sourceType,
lib: providedOptions?.lib ?? ['esnext'],
emitDecoratorMetadata: providedOptions?.emitDecoratorMetadata ??
DEFAULT_OPTIONS.emitDecoratorMetadata,
};
// ensure the option is lower cased
options.lib = options.lib.map(l => l.toLowerCase());
const scopeManager = new ScopeManager_1.ScopeManager(options);
const referencer = new referencer_1.Referencer(options, scopeManager);
referencer.visit(tree);
return scopeManager;
}
exports.analyze = analyze;
//# sourceMappingURL=analyze.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":";;;AACA,kEAA8D;AAG9D,6CAA0C;AAC1C,iDAA8C;AA6D9C,MAAM,eAAe,GAA6B;IAChD,gBAAgB,EAAE,0BAAW;IAC7B,YAAY,EAAE,KAAK;IACnB,aAAa,EAAE,KAAK;IACpB,SAAS,EAAE,OAAO;IAClB,eAAe,EAAE,IAAI;IACrB,GAAG,EAAE,CAAC,QAAQ,CAAC;IACf,UAAU,EAAE,QAAQ;IACpB,qBAAqB,EAAE,KAAK;CAC7B,CAAC;AAEF;;GAEG;AACH,SAAS,OAAO,CACd,IAAmB,EACnB,eAAgC;IAEhC,MAAM,OAAO,GAA6B;QACxC,gBAAgB,EACd,eAAe,EAAE,gBAAgB,IAAI,eAAe,CAAC,gBAAgB;QACvE,YAAY,EAAE,eAAe,EAAE,YAAY,IAAI,eAAe,CAAC,YAAY;QAC3E,aAAa,EACX,eAAe,EAAE,aAAa,IAAI,eAAe,CAAC,aAAa;QACjE,SAAS,EACP,eAAe,EAAE,SAAS,KAAK,SAAS;YACtC,CAAC,CAAC,eAAe,CAAC,SAAS;YAC3B,CAAC,CAAC,eAAe,CAAC,SAAS;QAC/B,eAAe,EACb,eAAe,EAAE,eAAe,IAAI,eAAe,CAAC,eAAe;QACrE,UAAU,EAAE,eAAe,EAAE,UAAU,IAAI,eAAe,CAAC,UAAU;QACrE,GAAG,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QACvC,qBAAqB,EACnB,eAAe,EAAE,qBAAqB;YACtC,eAAe,CAAC,qBAAqB;KACxC,CAAC;IAEF,mCAAmC;IACnC,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAS,CAAC,CAAC;IAE3D,MAAM,YAAY,GAAG,IAAI,2BAAY,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,uBAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAEzD,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,YAAY,CAAC;AACtB,CAAC;AAEQ,0BAAO"}

View File

@@ -0,0 +1,3 @@
declare function assert(value: unknown, message?: string): asserts value;
export { assert };
//# sourceMappingURL=assert.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AACA,iBAAS,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAI/D;AAED,OAAO,EAAE,MAAM,EAAE,CAAC"}

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assert = void 0;
// the base assert module doesn't use ts assertion syntax
function assert(value, message) {
if (value == null) {
throw new Error(message);
}
}
exports.assert = assert;
//# sourceMappingURL=assert.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":";;;AAAA,yDAAyD;AACzD,SAAS,MAAM,CAAC,KAAc,EAAE,OAAgB;IAC9C,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAEQ,wBAAM"}

View File

@@ -0,0 +1,10 @@
import type { TSESTree } from '@typescript-eslint/types';
import { DefinitionBase } from './DefinitionBase';
import { DefinitionType } from './DefinitionType';
declare class CatchClauseDefinition extends DefinitionBase<DefinitionType.CatchClause, TSESTree.CatchClause, null, TSESTree.BindingName> {
constructor(name: TSESTree.BindingName, node: CatchClauseDefinition['node']);
readonly isTypeDefinition = false;
readonly isVariableDefinition = true;
}
export { CatchClauseDefinition };
//# sourceMappingURL=CatchClauseDefinition.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CatchClauseDefinition.d.ts","sourceRoot":"","sources":["../../src/definition/CatchClauseDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,cAAM,qBAAsB,SAAQ,cAAc,CAChD,cAAc,CAAC,WAAW,EAC1B,QAAQ,CAAC,WAAW,EACpB,IAAI,EACJ,QAAQ,CAAC,WAAW,CACrB;gBACa,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC;IAI3E,SAAgB,gBAAgB,SAAS;IACzC,SAAgB,oBAAoB,QAAQ;CAC7C;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC"}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CatchClauseDefinition = void 0;
const DefinitionBase_1 = require("./DefinitionBase");
const DefinitionType_1 = require("./DefinitionType");
class CatchClauseDefinition extends DefinitionBase_1.DefinitionBase {
constructor(name, node) {
super(DefinitionType_1.DefinitionType.CatchClause, name, node, null);
this.isTypeDefinition = false;
this.isVariableDefinition = true;
}
}
exports.CatchClauseDefinition = CatchClauseDefinition;
//# sourceMappingURL=CatchClauseDefinition.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CatchClauseDefinition.js","sourceRoot":"","sources":["../../src/definition/CatchClauseDefinition.ts"],"names":[],"mappings":";;;AAEA,qDAAkD;AAClD,qDAAkD;AAElD,MAAM,qBAAsB,SAAQ,+BAKnC;IACC,YAAY,IAA0B,EAAE,IAAmC;QACzE,KAAK,CAAC,+BAAc,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAGtC,qBAAgB,GAAG,KAAK,CAAC;QACzB,yBAAoB,GAAG,IAAI,CAAC;IAH5C,CAAC;CAIF;AAEQ,sDAAqB"}

View File

@@ -0,0 +1,10 @@
import type { TSESTree } from '@typescript-eslint/types';
import { DefinitionBase } from './DefinitionBase';
import { DefinitionType } from './DefinitionType';
declare class ClassNameDefinition extends DefinitionBase<DefinitionType.ClassName, TSESTree.ClassDeclaration | TSESTree.ClassExpression, null, TSESTree.Identifier> {
constructor(name: TSESTree.Identifier, node: ClassNameDefinition['node']);
readonly isTypeDefinition = true;
readonly isVariableDefinition = true;
}
export { ClassNameDefinition };
//# sourceMappingURL=ClassNameDefinition.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ClassNameDefinition.d.ts","sourceRoot":"","sources":["../../src/definition/ClassNameDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,cAAM,mBAAoB,SAAQ,cAAc,CAC9C,cAAc,CAAC,SAAS,EACxB,QAAQ,CAAC,gBAAgB,GAAG,QAAQ,CAAC,eAAe,EACpD,IAAI,EACJ,QAAQ,CAAC,UAAU,CACpB;gBACa,IAAI,EAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC;IAIxE,SAAgB,gBAAgB,QAAQ;IACxC,SAAgB,oBAAoB,QAAQ;CAC7C;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClassNameDefinition = void 0;
const DefinitionBase_1 = require("./DefinitionBase");
const DefinitionType_1 = require("./DefinitionType");
class ClassNameDefinition extends DefinitionBase_1.DefinitionBase {
constructor(name, node) {
super(DefinitionType_1.DefinitionType.ClassName, name, node, null);
this.isTypeDefinition = true;
this.isVariableDefinition = true;
}
}
exports.ClassNameDefinition = ClassNameDefinition;
//# sourceMappingURL=ClassNameDefinition.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ClassNameDefinition.js","sourceRoot":"","sources":["../../src/definition/ClassNameDefinition.ts"],"names":[],"mappings":";;;AAEA,qDAAkD;AAClD,qDAAkD;AAElD,MAAM,mBAAoB,SAAQ,+BAKjC;IACC,YAAY,IAAyB,EAAE,IAAiC;QACtE,KAAK,CAAC,+BAAc,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAGpC,qBAAgB,GAAG,IAAI,CAAC;QACxB,yBAAoB,GAAG,IAAI,CAAC;IAH5C,CAAC;CAIF;AAEQ,kDAAmB"}

View File

@@ -0,0 +1,14 @@
import type { CatchClauseDefinition } from './CatchClauseDefinition';
import type { ClassNameDefinition } from './ClassNameDefinition';
import type { FunctionNameDefinition } from './FunctionNameDefinition';
import type { ImplicitGlobalVariableDefinition } from './ImplicitGlobalVariableDefinition';
import type { ImportBindingDefinition } from './ImportBindingDefinition';
import type { ParameterDefinition } from './ParameterDefinition';
import type { TSEnumMemberDefinition } from './TSEnumMemberDefinition';
import type { TSEnumNameDefinition } from './TSEnumNameDefinition';
import type { TSModuleNameDefinition } from './TSModuleNameDefinition';
import type { TypeDefinition } from './TypeDefinition';
import type { VariableDefinition } from './VariableDefinition';
type Definition = CatchClauseDefinition | ClassNameDefinition | FunctionNameDefinition | ImplicitGlobalVariableDefinition | ImportBindingDefinition | ParameterDefinition | TSEnumMemberDefinition | TSEnumNameDefinition | TSModuleNameDefinition | TypeDefinition | VariableDefinition;
export { Definition };
//# sourceMappingURL=Definition.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Definition.d.ts","sourceRoot":"","sources":["../../src/definition/Definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,KAAK,UAAU,GACX,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,gCAAgC,GAChC,uBAAuB,GACvB,mBAAmB,GACnB,sBAAsB,GACtB,oBAAoB,GACpB,sBAAsB,GACtB,cAAc,GACd,kBAAkB,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,CAAC"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=Definition.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Definition.js","sourceRoot":"","sources":["../../src/definition/Definition.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,39 @@
import type { TSESTree } from '@typescript-eslint/types';
import type { DefinitionType } from './DefinitionType';
declare abstract class DefinitionBase<TType extends DefinitionType, TNode extends TSESTree.Node, TParent extends TSESTree.Node | null, TName extends TSESTree.Node> {
/**
* A unique ID for this instance - primarily used to help debugging and testing
*/
readonly $id: number;
/**
* The type of the definition
* @public
*/
readonly type: TType;
/**
* The `Identifier` node of this definition
* @public
*/
readonly name: TName;
/**
* The enclosing node of the name.
* @public
*/
readonly node: TNode;
/**
* the enclosing statement node of the identifier.
* @public
*/
readonly parent: TParent;
constructor(type: TType, name: TName, node: TNode, parent: TParent);
/**
* `true` if the variable is valid in a type context, false otherwise
*/
abstract readonly isTypeDefinition: boolean;
/**
* `true` if the variable is valid in a value context, false otherwise
*/
abstract readonly isVariableDefinition: boolean;
}
export { DefinitionBase };
//# sourceMappingURL=DefinitionBase.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DefinitionBase.d.ts","sourceRoot":"","sources":["../../src/definition/DefinitionBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,uBAAe,cAAc,CAC3B,KAAK,SAAS,cAAc,EAC5B,KAAK,SAAS,QAAQ,CAAC,IAAI,EAC3B,OAAO,SAAS,QAAQ,CAAC,IAAI,GAAG,IAAI,EACpC,KAAK,SAAS,QAAQ,CAAC,IAAI;IAE3B;;OAEG;IACH,SAAgB,GAAG,EAAE,MAAM,CAAe;IAE1C;;;OAGG;IACH,SAAgB,IAAI,EAAE,KAAK,CAAC;IAE5B;;;OAGG;IACH,SAAgB,IAAI,EAAE,KAAK,CAAC;IAE5B;;;OAGG;IACH,SAAgB,IAAI,EAAE,KAAK,CAAC;IAE5B;;;OAGG;IACH,SAAgB,MAAM,EAAE,OAAO,CAAC;gBAEpB,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO;IAOlE;;OAEG;IACH,kBAAyB,gBAAgB,EAAE,OAAO,CAAC;IAEnD;;OAEG;IACH,kBAAyB,oBAAoB,EAAE,OAAO,CAAC;CACxD;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefinitionBase = void 0;
const ID_1 = require("../ID");
const generator = (0, ID_1.createIdGenerator)();
class DefinitionBase {
constructor(type, name, node, parent) {
/**
* A unique ID for this instance - primarily used to help debugging and testing
*/
this.$id = generator();
this.type = type;
this.name = name;
this.node = node;
this.parent = parent;
}
}
exports.DefinitionBase = DefinitionBase;
//# sourceMappingURL=DefinitionBase.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DefinitionBase.js","sourceRoot":"","sources":["../../src/definition/DefinitionBase.ts"],"names":[],"mappings":";;;AAEA,8BAA0C;AAG1C,MAAM,SAAS,GAAG,IAAA,sBAAiB,GAAE,CAAC;AAEtC,MAAe,cAAc;IAmC3B,YAAY,IAAW,EAAE,IAAW,EAAE,IAAW,EAAE,MAAe;QA7BlE;;WAEG;QACa,QAAG,GAAW,SAAS,EAAE,CAAC;QA2BxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CAWF;AAEQ,wCAAc"}

View File

@@ -0,0 +1,15 @@
declare enum DefinitionType {
CatchClause = "CatchClause",
ClassName = "ClassName",
FunctionName = "FunctionName",
ImplicitGlobalVariable = "ImplicitGlobalVariable",
ImportBinding = "ImportBinding",
Parameter = "Parameter",
TSEnumName = "TSEnumName",
TSEnumMember = "TSEnumMemberName",
TSModuleName = "TSModuleName",
Type = "Type",
Variable = "Variable"
}
export { DefinitionType };
//# sourceMappingURL=DefinitionType.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DefinitionType.d.ts","sourceRoot":"","sources":["../../src/definition/DefinitionType.ts"],"names":[],"mappings":"AAAA,aAAK,cAAc;IACjB,WAAW,gBAAgB;IAC3B,SAAS,cAAc;IACvB,YAAY,iBAAiB;IAC7B,sBAAsB,2BAA2B;IACjD,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,UAAU,eAAe;IACzB,YAAY,qBAAqB;IACjC,YAAY,iBAAiB;IAC7B,IAAI,SAAS;IACb,QAAQ,aAAa;CACtB;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefinitionType = void 0;
var DefinitionType;
(function (DefinitionType) {
DefinitionType["CatchClause"] = "CatchClause";
DefinitionType["ClassName"] = "ClassName";
DefinitionType["FunctionName"] = "FunctionName";
DefinitionType["ImplicitGlobalVariable"] = "ImplicitGlobalVariable";
DefinitionType["ImportBinding"] = "ImportBinding";
DefinitionType["Parameter"] = "Parameter";
DefinitionType["TSEnumName"] = "TSEnumName";
DefinitionType["TSEnumMember"] = "TSEnumMemberName";
DefinitionType["TSModuleName"] = "TSModuleName";
DefinitionType["Type"] = "Type";
DefinitionType["Variable"] = "Variable";
})(DefinitionType || (exports.DefinitionType = DefinitionType = {}));
//# sourceMappingURL=DefinitionType.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DefinitionType.js","sourceRoot":"","sources":["../../src/definition/DefinitionType.ts"],"names":[],"mappings":";;;AAAA,IAAK,cAYJ;AAZD,WAAK,cAAc;IACjB,6CAA2B,CAAA;IAC3B,yCAAuB,CAAA;IACvB,+CAA6B,CAAA;IAC7B,mEAAiD,CAAA;IACjD,iDAA+B,CAAA;IAC/B,yCAAuB,CAAA;IACvB,2CAAyB,CAAA;IACzB,mDAAiC,CAAA;IACjC,+CAA6B,CAAA;IAC7B,+BAAa,CAAA;IACb,uCAAqB,CAAA;AACvB,CAAC,EAZI,cAAc,8BAAd,cAAc,QAYlB"}

View File

@@ -0,0 +1,10 @@
import type { TSESTree } from '@typescript-eslint/types';
import { DefinitionBase } from './DefinitionBase';
import { DefinitionType } from './DefinitionType';
declare class FunctionNameDefinition extends DefinitionBase<DefinitionType.FunctionName, TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.TSDeclareFunction | TSESTree.TSEmptyBodyFunctionExpression, null, TSESTree.Identifier> {
constructor(name: TSESTree.Identifier, node: FunctionNameDefinition['node']);
readonly isTypeDefinition = false;
readonly isVariableDefinition = true;
}
export { FunctionNameDefinition };
//# sourceMappingURL=FunctionNameDefinition.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FunctionNameDefinition.d.ts","sourceRoot":"","sources":["../../src/definition/FunctionNameDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,cAAM,sBAAuB,SAAQ,cAAc,CACjD,cAAc,CAAC,YAAY,EACzB,QAAQ,CAAC,mBAAmB,GAC5B,QAAQ,CAAC,kBAAkB,GAC3B,QAAQ,CAAC,iBAAiB,GAC1B,QAAQ,CAAC,6BAA6B,EACxC,IAAI,EACJ,QAAQ,CAAC,UAAU,CACpB;gBACa,IAAI,EAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,sBAAsB,CAAC,MAAM,CAAC;IAI3E,SAAgB,gBAAgB,SAAS;IACzC,SAAgB,oBAAoB,QAAQ;CAC7C;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FunctionNameDefinition = void 0;
const DefinitionBase_1 = require("./DefinitionBase");
const DefinitionType_1 = require("./DefinitionType");
class FunctionNameDefinition extends DefinitionBase_1.DefinitionBase {
constructor(name, node) {
super(DefinitionType_1.DefinitionType.FunctionName, name, node, null);
this.isTypeDefinition = false;
this.isVariableDefinition = true;
}
}
exports.FunctionNameDefinition = FunctionNameDefinition;
//# sourceMappingURL=FunctionNameDefinition.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FunctionNameDefinition.js","sourceRoot":"","sources":["../../src/definition/FunctionNameDefinition.ts"],"names":[],"mappings":";;;AAEA,qDAAkD;AAClD,qDAAkD;AAElD,MAAM,sBAAuB,SAAQ,+BAQpC;IACC,YAAY,IAAyB,EAAE,IAAoC;QACzE,KAAK,CAAC,+BAAc,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAGvC,qBAAgB,GAAG,KAAK,CAAC;QACzB,yBAAoB,GAAG,IAAI,CAAC;IAH5C,CAAC;CAIF;AAEQ,wDAAsB"}

Some files were not shown because too many files have changed in this diff Show More