@murky-web/oxlint-plugin-solid 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/package.json +44 -0
- package/src/compat.mjs +53 -0
- package/src/index.mjs +56 -0
- package/src/rules/components_return_once.mjs +202 -0
- package/src/rules/event_handlers.mjs +298 -0
- package/src/rules/imports.mjs +205 -0
- package/src/rules/jsx_no_duplicate_props.mjs +87 -0
- package/src/rules/jsx_no_script_url.mjs +54 -0
- package/src/rules/jsx_no_undef.mjs +217 -0
- package/src/rules/jsx_uses_vars.mjs +55 -0
- package/src/rules/no_array_handlers.mjs +53 -0
- package/src/rules/no_destructure.mjs +210 -0
- package/src/rules/no_innerhtml.mjs +145 -0
- package/src/rules/no_proxy_apis.mjs +96 -0
- package/src/rules/no_react_deps.mjs +65 -0
- package/src/rules/no_react_specific_props.mjs +71 -0
- package/src/rules/no_unknown_namespaces.mjs +100 -0
- package/src/rules/prefer_arrow_components.mjs +411 -0
- package/src/rules/prefer_classlist.mjs +89 -0
- package/src/rules/prefer_for.mjs +92 -0
- package/src/rules/prefer_show.mjs +92 -0
- package/src/rules/reactivity.mjs +1300 -0
- package/src/rules/self_closing_comp.mjs +153 -0
- package/src/rules/style_prop.mjs +155 -0
- package/src/rules/validate_jsx_nesting.mjs +16 -0
- package/src/utils.mjs +337 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @murky-web/oxlint-plugin-solid
|
|
2
|
+
|
|
3
|
+
Workspace-Paket fuer den lokalen Oxlint-JS-Plugin-Port der Solid-Regeln.
|
|
4
|
+
|
|
5
|
+
Es enthaelt die Solid-Regeln lokal im Paket und erweitert den Upstream-Satz um
|
|
6
|
+
projektspezifische Regeln wie `solid/prefer-arrow-components`.
|
|
7
|
+
|
|
8
|
+
Die Regelmodule unter `src/rules/` sind aus dem Upstream-Quellstand abgeleitet
|
|
9
|
+
und laufen ohne `eslint-plugin-solid` als Zielprojekt-Dependency.
|
|
10
|
+
|
|
11
|
+
Aktuell sind enthalten:
|
|
12
|
+
|
|
13
|
+
- die komplette von `eslint-plugin-solid` exportierte Regelmenge
|
|
14
|
+
- die zusaetzliche Projektregel `solid/prefer-arrow-components`
|
|
15
|
+
- ein Test-Harness, der die exportierte Rule-Surface und echte Diagnostik
|
|
16
|
+
gegen Temp-Projekte prueft
|
|
17
|
+
|
|
18
|
+
Aktuell wird das Paket nicht direkt im Zielprojekt installiert. Stattdessen
|
|
19
|
+
kopiert `@murky-web/config` die Rule-Runtime in `./oxc/jsplugins/solid/`, damit
|
|
20
|
+
Oxlint sie ueber einen lokalen `jsPlugins`-Pfad laden kann.
|
|
21
|
+
|
|
22
|
+
Wichtig dabei:
|
|
23
|
+
|
|
24
|
+
- `@murky-web/config` loest dieses Paket als normale Workspace-Dependency auf
|
|
25
|
+
- der Installer kopiert den lokalen Plugin-Src-Ordner aus der installierten
|
|
26
|
+
Paketauflösung
|
|
27
|
+
- Zielprojekte brauchen dadurch weiter nur die kopierte Runtime unter
|
|
28
|
+
`./oxc/jsplugins/solid/`, nicht dieses Paket als direkte Dependency
|
|
29
|
+
|
|
30
|
+
## Nutzung ueber @murky-web/config
|
|
31
|
+
|
|
32
|
+
Im Zielprojekt:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
web-dev-config init --oxc --typescript --frontend-solid
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Danach:
|
|
39
|
+
|
|
40
|
+
- liegt die lokale Rule-Runtime unter `./oxc/jsplugins/solid/`
|
|
41
|
+
- wird `./linting/solid.jsonc` in die Oxc-Konfiguration eingehängt
|
|
42
|
+
- zeigt `jsPlugins` auf `./jsplugins/solid/index.mjs`
|
|
43
|
+
- feuern sowohl die portierten Solid-Regeln als auch
|
|
44
|
+
`solid/prefer-arrow-components`
|
|
45
|
+
|
|
46
|
+
Ein typischer Fix-Fall:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
export function Card(props: Props) {
|
|
50
|
+
return <section>{props.children}</section>;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
wird mit `oxlint --fix` zu:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import type { ParentComponent } from "solid-js";
|
|
58
|
+
|
|
59
|
+
export const Card: ParentComponent<Props> = (props) => {
|
|
60
|
+
return <section>{props.children}</section>;
|
|
61
|
+
};
|
|
62
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@murky-web/oxlint-plugin-solid",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Local Oxlint JS plugin port for Solid rules plus Murky-specific Solid conventions.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"README.md",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "bun test ./tests"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/MurkyTheMurloc/web_dev_tools.git",
|
|
21
|
+
"directory": "packages/oxlint-plugin-solid"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/MurkyTheMurloc/web_dev_tools/tree/main/packages/oxlint-plugin-solid",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/MurkyTheMurloc/web_dev_tools/issues"
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@typescript-eslint/utils": "^8.57.1",
|
|
30
|
+
"eslint": "^9.38.0",
|
|
31
|
+
"estraverse": "^5.3.0",
|
|
32
|
+
"is-html": "^2.0.0",
|
|
33
|
+
"kebab-case": "^1.0.2",
|
|
34
|
+
"known-css-properties": "^0.30.0",
|
|
35
|
+
"style-to-object": "^1.0.8"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"solid-js": "^1.9.11"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"provenance": true
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/compat.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ASTUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
export function getSourceCode(context) {
|
|
4
|
+
if (typeof context.getSourceCode === "function") {
|
|
5
|
+
return context.getSourceCode();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return context.sourceCode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getScope(context, node) {
|
|
12
|
+
const sourceCode = getSourceCode(context);
|
|
13
|
+
|
|
14
|
+
if (typeof sourceCode.getScope === "function") {
|
|
15
|
+
return sourceCode.getScope(node);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof context.getScope === "function") {
|
|
19
|
+
return context.getScope();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return context.sourceCode.getScope(node);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function findVariable(context, node) {
|
|
26
|
+
return ASTUtils.findVariable(getScope(context, node), node);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function markVariableAsUsed(context, name, node) {
|
|
30
|
+
if (typeof context.markVariableAsUsed === "function") {
|
|
31
|
+
context.markVariableAsUsed(name);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sourceCode = getSourceCode(context);
|
|
36
|
+
if (typeof sourceCode.markVariableAsUsed !== "function") {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
sourceCode.markVariableAsUsed(name, node);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (
|
|
44
|
+
error instanceof Error &&
|
|
45
|
+
error.message.includes("markVariableAsUsed") &&
|
|
46
|
+
error.message.includes("not implemented")
|
|
47
|
+
) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import componentsReturnOnceRule from "./rules/components_return_once.mjs";
|
|
2
|
+
import eventHandlersRule from "./rules/event_handlers.mjs";
|
|
3
|
+
import importsRule from "./rules/imports.mjs";
|
|
4
|
+
import jsxNoDuplicatePropsRule from "./rules/jsx_no_duplicate_props.mjs";
|
|
5
|
+
import { jsxNoScriptUrlRule } from "./rules/jsx_no_script_url.mjs";
|
|
6
|
+
import jsxNoUndefRule from "./rules/jsx_no_undef.mjs";
|
|
7
|
+
import { jsxUsesVarsRule } from "./rules/jsx_uses_vars.mjs";
|
|
8
|
+
import { noArrayHandlersRule } from "./rules/no_array_handlers.mjs";
|
|
9
|
+
import noDestructureRule from "./rules/no_destructure.mjs";
|
|
10
|
+
import noInnerhtmlRule from "./rules/no_innerhtml.mjs";
|
|
11
|
+
import noProxyApisRule from "./rules/no_proxy_apis.mjs";
|
|
12
|
+
import noReactDepsRule from "./rules/no_react_deps.mjs";
|
|
13
|
+
import { noReactSpecificPropsRule } from "./rules/no_react_specific_props.mjs";
|
|
14
|
+
import noUnknownNamespacesRule from "./rules/no_unknown_namespaces.mjs";
|
|
15
|
+
import { preferArrowComponentsRule } from "./rules/prefer_arrow_components.mjs";
|
|
16
|
+
import preferClasslistRule from "./rules/prefer_classlist.mjs";
|
|
17
|
+
import preferForRule from "./rules/prefer_for.mjs";
|
|
18
|
+
import preferShowRule from "./rules/prefer_show.mjs";
|
|
19
|
+
import reactivityRule from "./rules/reactivity.mjs";
|
|
20
|
+
import selfClosingCompRule from "./rules/self_closing_comp.mjs";
|
|
21
|
+
import stylePropRule from "./rules/style_prop.mjs";
|
|
22
|
+
import validateJsxNestingRule from "./rules/validate_jsx_nesting.mjs";
|
|
23
|
+
|
|
24
|
+
const extendedPlugin = {
|
|
25
|
+
meta: {
|
|
26
|
+
name: "solid",
|
|
27
|
+
version: "0.14.5",
|
|
28
|
+
},
|
|
29
|
+
rules: {
|
|
30
|
+
"components-return-once": componentsReturnOnceRule,
|
|
31
|
+
"event-handlers": eventHandlersRule,
|
|
32
|
+
imports: importsRule,
|
|
33
|
+
"jsx-no-duplicate-props": jsxNoDuplicatePropsRule,
|
|
34
|
+
"jsx-no-script-url": jsxNoScriptUrlRule,
|
|
35
|
+
"jsx-no-undef": jsxNoUndefRule,
|
|
36
|
+
"jsx-uses-vars": jsxUsesVarsRule,
|
|
37
|
+
"no-array-handlers": noArrayHandlersRule,
|
|
38
|
+
"no-destructure": noDestructureRule,
|
|
39
|
+
"no-innerhtml": noInnerhtmlRule,
|
|
40
|
+
"no-proxy-apis": noProxyApisRule,
|
|
41
|
+
"no-react-deps": noReactDepsRule,
|
|
42
|
+
"no-react-specific-props": noReactSpecificPropsRule,
|
|
43
|
+
"no-unknown-namespaces": noUnknownNamespacesRule,
|
|
44
|
+
"prefer-arrow-components": preferArrowComponentsRule,
|
|
45
|
+
"prefer-classlist": preferClasslistRule,
|
|
46
|
+
"prefer-for": preferForRule,
|
|
47
|
+
"prefer-show": preferShowRule,
|
|
48
|
+
reactivity: reactivityRule,
|
|
49
|
+
"self-closing-comp": selfClosingCompRule,
|
|
50
|
+
"style-prop": stylePropRule,
|
|
51
|
+
"validate-jsx-nesting": validateJsxNestingRule,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// oxlint-disable-next-line import/no-default-export -- Oxlint JS plugins require a default export surface.
|
|
56
|
+
export default extendedPlugin;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { getSourceCode } from "../compat.mjs";
|
|
4
|
+
import { getFunctionName } from "../utils.mjs";
|
|
5
|
+
const createRule = ESLintUtils.RuleCreator.withoutDocs;
|
|
6
|
+
const isNothing = (node) => {
|
|
7
|
+
if (!node) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
switch (node.type) {
|
|
11
|
+
case "Literal":
|
|
12
|
+
return [null, undefined, false, ""].includes(node.value);
|
|
13
|
+
case "JSXFragment":
|
|
14
|
+
return !node.children || node.children.every(isNothing);
|
|
15
|
+
default:
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const getLineLength = (loc) => loc.end.line - loc.start.line + 1;
|
|
20
|
+
export default createRule({
|
|
21
|
+
meta: {
|
|
22
|
+
type: "problem",
|
|
23
|
+
docs: {
|
|
24
|
+
description:
|
|
25
|
+
"Disallow early returns in components. Solid components only run once, and so conditionals should be inside JSX.",
|
|
26
|
+
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/packages/eslint-plugin-solid/docs/components-return-once.md",
|
|
27
|
+
},
|
|
28
|
+
fixable: "code",
|
|
29
|
+
schema: [],
|
|
30
|
+
messages: {
|
|
31
|
+
noEarlyReturn:
|
|
32
|
+
"Solid components run once, so an early return breaks reactivity. Move the condition inside a JSX element, such as a fragment or <Show />.",
|
|
33
|
+
noConditionalReturn:
|
|
34
|
+
"Solid components run once, so a conditional return breaks reactivity. Move the condition inside a JSX element, such as a fragment or <Show />.",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [],
|
|
38
|
+
create(context) {
|
|
39
|
+
const functionStack = [];
|
|
40
|
+
const putIntoJSX = (node) => {
|
|
41
|
+
const text = getSourceCode(context).getText(node);
|
|
42
|
+
return node.type === "JSXElement" || node.type === "JSXFragment"
|
|
43
|
+
? text
|
|
44
|
+
: `{${text}}`;
|
|
45
|
+
};
|
|
46
|
+
const currentFunction = () => functionStack[functionStack.length - 1];
|
|
47
|
+
const onFunctionEnter = (node) => {
|
|
48
|
+
let lastReturn;
|
|
49
|
+
if (node.body.type === "BlockStatement") {
|
|
50
|
+
// find last statement, ignoring function/class/variable declarations (hoisting)
|
|
51
|
+
const last = node.body.body.findLast(
|
|
52
|
+
(node) => !node.type.endsWith("Declaration"),
|
|
53
|
+
);
|
|
54
|
+
// if it's a return, store it
|
|
55
|
+
if (last && last.type === "ReturnStatement") {
|
|
56
|
+
lastReturn = last;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
functionStack.push({
|
|
60
|
+
isComponent: false,
|
|
61
|
+
lastReturn,
|
|
62
|
+
earlyReturns: [],
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const onFunctionExit = (node) => {
|
|
66
|
+
if (
|
|
67
|
+
// "render props" aren't components
|
|
68
|
+
getFunctionName(node)?.match(/^[a-z]/) ||
|
|
69
|
+
node.parent?.type === "JSXExpressionContainer" ||
|
|
70
|
+
// ignore createMemo(() => conditional JSX), report HOC(() => conditional JSX)
|
|
71
|
+
(node.parent?.type === "CallExpression" &&
|
|
72
|
+
node.parent.arguments.some((n) => n === node) &&
|
|
73
|
+
!node.parent.callee.name?.match(/^[A-Z]/))
|
|
74
|
+
) {
|
|
75
|
+
currentFunction().isComponent = false;
|
|
76
|
+
}
|
|
77
|
+
if (currentFunction().isComponent) {
|
|
78
|
+
// Warn on each early return
|
|
79
|
+
currentFunction().earlyReturns.forEach((earlyReturn) => {
|
|
80
|
+
context.report({
|
|
81
|
+
node: earlyReturn,
|
|
82
|
+
messageId: "noEarlyReturn",
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
const argument = currentFunction().lastReturn?.argument;
|
|
86
|
+
if (argument?.type === "ConditionalExpression") {
|
|
87
|
+
const sourceCode = getSourceCode(context);
|
|
88
|
+
context.report({
|
|
89
|
+
node: argument.parent,
|
|
90
|
+
messageId: "noConditionalReturn",
|
|
91
|
+
fix: (fixer) => {
|
|
92
|
+
const { test, consequent, alternate } = argument;
|
|
93
|
+
const conditions = [{ test, consequent }];
|
|
94
|
+
let fallback = alternate;
|
|
95
|
+
while (fallback.type === "ConditionalExpression") {
|
|
96
|
+
conditions.push({
|
|
97
|
+
test: fallback.test,
|
|
98
|
+
consequent: fallback.consequent,
|
|
99
|
+
});
|
|
100
|
+
fallback = fallback.alternate;
|
|
101
|
+
}
|
|
102
|
+
if (conditions.length >= 2) {
|
|
103
|
+
// we have a nested ternary, use <Switch><Match /></Switch>
|
|
104
|
+
const fallbackStr = !isNothing(fallback)
|
|
105
|
+
? ` fallback={${sourceCode.getText(fallback)}}`
|
|
106
|
+
: "";
|
|
107
|
+
return fixer.replaceText(
|
|
108
|
+
argument,
|
|
109
|
+
`<Switch${fallbackStr}>\n${conditions
|
|
110
|
+
.map(
|
|
111
|
+
({ test, consequent }) =>
|
|
112
|
+
`<Match when={${sourceCode.getText(test)}}>${putIntoJSX(consequent)}</Match>`,
|
|
113
|
+
)
|
|
114
|
+
.join("\n")}\n</Switch>`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (isNothing(consequent)) {
|
|
118
|
+
// we have a single ternary and the consequent is nothing. Negate the condition and use a <Show>.
|
|
119
|
+
return fixer.replaceText(
|
|
120
|
+
argument,
|
|
121
|
+
`<Show when={!(${sourceCode.getText(test)})}>${putIntoJSX(alternate)}</Show>`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
isNothing(fallback) ||
|
|
126
|
+
getLineLength(consequent.loc) >=
|
|
127
|
+
getLineLength(alternate.loc) * 1.5
|
|
128
|
+
) {
|
|
129
|
+
// we have a standard ternary, and the alternate is a bit shorter in LOC than the consequent, which
|
|
130
|
+
// should be enough to tell that it's logically a fallback instead of an equal branch.
|
|
131
|
+
const fallbackStr = !isNothing(fallback)
|
|
132
|
+
? ` fallback={${sourceCode.getText(fallback)}}`
|
|
133
|
+
: "";
|
|
134
|
+
return fixer.replaceText(
|
|
135
|
+
argument,
|
|
136
|
+
`<Show when={${sourceCode.getText(test)}}${fallbackStr}>${putIntoJSX(consequent)}</Show>`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
// we have a standard ternary, but no signal from the user as to which branch is the "fallback" and
|
|
140
|
+
// which is the children. Move the whole conditional inside a JSX fragment.
|
|
141
|
+
return fixer.replaceText(
|
|
142
|
+
argument,
|
|
143
|
+
`<>${putIntoJSX(argument)}</>`,
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
} else if (argument?.type === "LogicalExpression") {
|
|
148
|
+
if (argument.operator === "&&") {
|
|
149
|
+
const sourceCode = getSourceCode(context);
|
|
150
|
+
// we have a `return condition && expression`--put that in a <Show />
|
|
151
|
+
context.report({
|
|
152
|
+
node: argument,
|
|
153
|
+
messageId: "noConditionalReturn",
|
|
154
|
+
fix: (fixer) => {
|
|
155
|
+
const { left: test, right: consequent } =
|
|
156
|
+
argument;
|
|
157
|
+
return fixer.replaceText(
|
|
158
|
+
argument,
|
|
159
|
+
`<Show when={${sourceCode.getText(test)}}>${putIntoJSX(consequent)}</Show>`,
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
// we have some other kind of conditional, warn
|
|
165
|
+
context.report({
|
|
166
|
+
node: argument,
|
|
167
|
+
messageId: "noConditionalReturn",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Pop on exit
|
|
173
|
+
functionStack.pop();
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
FunctionDeclaration: onFunctionEnter,
|
|
177
|
+
FunctionExpression: onFunctionEnter,
|
|
178
|
+
ArrowFunctionExpression: onFunctionEnter,
|
|
179
|
+
"FunctionDeclaration:exit": onFunctionExit,
|
|
180
|
+
"FunctionExpression:exit": onFunctionExit,
|
|
181
|
+
"ArrowFunctionExpression:exit": onFunctionExit,
|
|
182
|
+
JSXElement() {
|
|
183
|
+
if (functionStack.length) {
|
|
184
|
+
currentFunction().isComponent = true;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
JSXFragment() {
|
|
188
|
+
if (functionStack.length) {
|
|
189
|
+
currentFunction().isComponent = true;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
ReturnStatement(node) {
|
|
193
|
+
if (
|
|
194
|
+
functionStack.length &&
|
|
195
|
+
node !== currentFunction().lastReturn
|
|
196
|
+
) {
|
|
197
|
+
currentFunction().earlyReturns.push(node);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
});
|