@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.
@@ -0,0 +1,96 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ import {
4
+ isFunctionNode,
5
+ trackImports,
6
+ isPropsByName,
7
+ trace,
8
+ } from "../utils.mjs";
9
+ const createRule = ESLintUtils.RuleCreator.withoutDocs;
10
+ export default createRule({
11
+ meta: {
12
+ type: "problem",
13
+ docs: {
14
+ description:
15
+ "Disallow usage of APIs that use ES6 Proxies, only to target environments that don't support them.",
16
+ url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/packages/eslint-plugin-solid/docs/no-proxy-apis.md",
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ noStore:
21
+ "Solid Store APIs use Proxies, which are incompatible with your target environment.",
22
+ spreadCall:
23
+ "Using a function call in JSX spread makes Solid use Proxies, which are incompatible with your target environment.",
24
+ spreadMember:
25
+ "Using a property access in JSX spread makes Solid use Proxies, which are incompatible with your target environment.",
26
+ proxyLiteral:
27
+ "Proxies are incompatible with your target environment.",
28
+ mergeProps:
29
+ "If you pass a function to `mergeProps`, it will create a Proxy, which are incompatible with your target environment.",
30
+ },
31
+ },
32
+ defaultOptions: [],
33
+ create(context) {
34
+ const { matchImport, handleImportDeclaration } = trackImports();
35
+ return {
36
+ ImportDeclaration(node) {
37
+ handleImportDeclaration(node); // track import aliases
38
+ const source = node.source.value;
39
+ if (source === "solid-js/store") {
40
+ context.report({
41
+ node,
42
+ messageId: "noStore",
43
+ });
44
+ }
45
+ },
46
+ "JSXSpreadAttribute MemberExpression"(node) {
47
+ context.report({ node, messageId: "spreadMember" });
48
+ },
49
+ "JSXSpreadAttribute CallExpression"(node) {
50
+ context.report({ node, messageId: "spreadCall" });
51
+ },
52
+ CallExpression(node) {
53
+ if (node.callee.type === "Identifier") {
54
+ if (matchImport("mergeProps", node.callee.name)) {
55
+ node.arguments
56
+ .filter((arg) => {
57
+ if (arg.type === "SpreadElement") return true;
58
+ const traced = trace(arg, context);
59
+ return (
60
+ (traced.type === "Identifier" &&
61
+ !isPropsByName(traced.name)) ||
62
+ isFunctionNode(traced)
63
+ );
64
+ })
65
+ .forEach((badArg) => {
66
+ context.report({
67
+ node: badArg,
68
+ messageId: "mergeProps",
69
+ });
70
+ });
71
+ }
72
+ } else if (node.callee.type === "MemberExpression") {
73
+ if (
74
+ node.callee.object.type === "Identifier" &&
75
+ node.callee.object.name === "Proxy" &&
76
+ node.callee.property.type === "Identifier" &&
77
+ node.callee.property.name === "revocable"
78
+ ) {
79
+ context.report({
80
+ node,
81
+ messageId: "proxyLiteral",
82
+ });
83
+ }
84
+ }
85
+ },
86
+ NewExpression(node) {
87
+ if (
88
+ node.callee.type === "Identifier" &&
89
+ node.callee.name === "Proxy"
90
+ ) {
91
+ context.report({ node, messageId: "proxyLiteral" });
92
+ }
93
+ },
94
+ };
95
+ },
96
+ });
@@ -0,0 +1,65 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ import { isFunctionNode, trace, trackImports } from "../utils.mjs";
4
+ const createRule = ESLintUtils.RuleCreator.withoutDocs;
5
+ export default createRule({
6
+ meta: {
7
+ type: "problem",
8
+ docs: {
9
+ description:
10
+ "Disallow usage of dependency arrays in `createEffect` and `createMemo`.",
11
+ url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/packages/eslint-plugin-solid/docs/no-react-deps.md",
12
+ },
13
+ fixable: "code",
14
+ schema: [],
15
+ messages: {
16
+ noUselessDep:
17
+ "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`.",
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ /** Tracks imports from 'solid-js', handling aliases. */
23
+ const { matchImport, handleImportDeclaration } = trackImports();
24
+ return {
25
+ ImportDeclaration: handleImportDeclaration,
26
+ CallExpression(node) {
27
+ if (
28
+ node.callee.type === "Identifier" &&
29
+ matchImport(
30
+ ["createEffect", "createMemo"],
31
+ node.callee.name,
32
+ ) &&
33
+ node.arguments.length === 2 &&
34
+ node.arguments.every((arg) => arg.type !== "SpreadElement")
35
+ ) {
36
+ // grab both arguments, tracing any variables to their actual values if possible
37
+ const [arg0, arg1] = node.arguments.map((arg) =>
38
+ trace(arg, context),
39
+ );
40
+ if (
41
+ isFunctionNode(arg0) &&
42
+ arg0.params.length === 0 &&
43
+ arg1.type === "ArrayExpression"
44
+ ) {
45
+ // A second argument that looks like a dependency array was passed to
46
+ // createEffect/createMemo, and the inline function doesn't accept a parameter, so it
47
+ // can't just be an initial value.
48
+ context.report({
49
+ node: node.arguments[1], // if this is a variable, highlight the usage, not the initialization
50
+ messageId: "noUselessDep",
51
+ data: {
52
+ name: node.callee.name,
53
+ },
54
+ // remove dep array if it's given inline, otherwise don't fix
55
+ fix:
56
+ arg1 === node.arguments[1]
57
+ ? (fixer) => fixer.remove(arg1)
58
+ : undefined,
59
+ });
60
+ }
61
+ }
62
+ },
63
+ };
64
+ },
65
+ });
@@ -0,0 +1,71 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ import { isDOMElementName, jsxGetProp, jsxHasProp } from "../utils.mjs";
4
+
5
+ const createRule = ESLintUtils.RuleCreator.withoutDocs;
6
+ const REACT_SPECIFIC_PROPS = [
7
+ { from: "className", to: "class" },
8
+ { from: "htmlFor", to: "for" },
9
+ ];
10
+
11
+ export const noReactSpecificPropsRule = createRule({
12
+ meta: {
13
+ docs: {
14
+ description:
15
+ "Disallow usage of React-specific className/htmlFor props.",
16
+ url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/packages/eslint-plugin-solid/docs/no-react-specific-props.md",
17
+ },
18
+ fixable: "code",
19
+ messages: {
20
+ noUselessKey:
21
+ "Elements in a <For> or <Index> list do not need a key prop.",
22
+ prefer: "Prefer the `{{ to }}` prop over the deprecated `{{ from }}` prop.",
23
+ },
24
+ schema: [],
25
+ type: "problem",
26
+ },
27
+ defaultOptions: [],
28
+ create(context) {
29
+ return {
30
+ JSXOpeningElement(node) {
31
+ for (const { from, to } of REACT_SPECIFIC_PROPS) {
32
+ const attribute = jsxGetProp(node.attributes, from);
33
+ if (!attribute) {
34
+ continue;
35
+ }
36
+
37
+ const fix = !jsxHasProp(node.attributes, to)
38
+ ? (fixer) => {
39
+ return fixer.replaceText(attribute.name, to);
40
+ }
41
+ : undefined;
42
+
43
+ context.report({
44
+ data: { from, to },
45
+ fix,
46
+ messageId: "prefer",
47
+ node: attribute,
48
+ });
49
+ }
50
+
51
+ if (
52
+ node.name.type === "JSXIdentifier" &&
53
+ isDOMElementName(node.name.name)
54
+ ) {
55
+ const keyProp = jsxGetProp(node.attributes, "key");
56
+ if (!keyProp) {
57
+ return;
58
+ }
59
+
60
+ context.report({
61
+ fix(fixer) {
62
+ return fixer.remove(keyProp);
63
+ },
64
+ messageId: "noUselessKey",
65
+ node: keyProp,
66
+ });
67
+ }
68
+ },
69
+ };
70
+ },
71
+ });
@@ -0,0 +1,100 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ import { isDOMElementName } from "../utils.mjs";
4
+ const createRule = ESLintUtils.RuleCreator.withoutDocs;
5
+ const knownNamespaces = ["on", "oncapture", "use", "prop", "attr", "bool"];
6
+ const styleNamespaces = ["style", "class"];
7
+ const otherNamespaces = ["xmlns", "xlink"];
8
+ export default createRule({
9
+ meta: {
10
+ type: "problem",
11
+ docs: {
12
+ description:
13
+ "Enforce using only Solid-specific namespaced attribute names (i.e. `'on:'` in `<div on:click={...} />`).",
14
+ url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/packages/eslint-plugin-solid/docs/no-unknown-namespaces.md",
15
+ },
16
+ hasSuggestions: true,
17
+ schema: [
18
+ {
19
+ type: "object",
20
+ properties: {
21
+ allowedNamespaces: {
22
+ description:
23
+ "an array of additional namespace names to allow",
24
+ type: "array",
25
+ items: {
26
+ type: "string",
27
+ },
28
+ default: [],
29
+ minItems: 1,
30
+ uniqueItems: true,
31
+ },
32
+ },
33
+ additionalProperties: false,
34
+ },
35
+ ],
36
+ messages: {
37
+ unknown: `'{{namespace}}:' is not one of Solid's special prefixes for JSX attributes (${knownNamespaces
38
+ .map((n) => `'${n}:'`)
39
+ .join(", ")}).`,
40
+ style: "Using the '{{namespace}}:' special prefix is potentially confusing, prefer the '{{namespace}}' prop instead.",
41
+ component: "Namespaced props have no effect on components.",
42
+ "component-suggest":
43
+ "Replace {{namespace}}:{{name}} with {{name}}.",
44
+ },
45
+ },
46
+ defaultOptions: [],
47
+ create(context) {
48
+ const explicitlyAllowedNamespaces =
49
+ context.options?.[0]?.allowedNamespaces;
50
+ return {
51
+ "JSXAttribute > JSXNamespacedName": (node) => {
52
+ const openingElement = node.parent.parent;
53
+ if (
54
+ openingElement.name.type === "JSXIdentifier" &&
55
+ !isDOMElementName(openingElement.name.name)
56
+ ) {
57
+ // no namespaces on Solid component elements
58
+ context.report({
59
+ node,
60
+ messageId: "component",
61
+ suggest: [
62
+ {
63
+ messageId: "component-suggest",
64
+ data: {
65
+ namespace: node.namespace.name,
66
+ name: node.name.name,
67
+ },
68
+ fix: (fixer) =>
69
+ fixer.replaceText(node, node.name.name),
70
+ },
71
+ ],
72
+ });
73
+ return;
74
+ }
75
+ const namespace = node.namespace?.name;
76
+ if (
77
+ !(
78
+ knownNamespaces.includes(namespace) ||
79
+ otherNamespaces.includes(namespace) ||
80
+ explicitlyAllowedNamespaces?.includes(namespace)
81
+ )
82
+ ) {
83
+ if (styleNamespaces.includes(namespace)) {
84
+ context.report({
85
+ node,
86
+ messageId: "style",
87
+ data: { namespace },
88
+ });
89
+ } else {
90
+ context.report({
91
+ node,
92
+ messageId: "unknown",
93
+ data: { namespace },
94
+ });
95
+ }
96
+ }
97
+ },
98
+ };
99
+ },
100
+ });