-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/no-proxy-apis'
- Loading branch information
Showing
8 changed files
with
313 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<!-- AUTO-GENERATED-CONTENT:START (HEADER) --> | ||
# solid/no-proxy-apis | ||
Disallow usage of APIs that use ES6 Proxies, only to target environments that don't support them. | ||
This rule is **off** by default. | ||
|
||
[View source](../src/rules/no-proxy-apis.ts) · [View tests](../test/rules/no-proxy-apis.test.ts) | ||
|
||
<!-- AUTO-GENERATED-CONTENT:END --> | ||
|
||
<!-- AUTO-GENERATED-CONTENT:START (OPTIONS) --> | ||
|
||
<!-- AUTO-GENERATED-CONTENT:END --> | ||
|
||
<!-- AUTO-GENERATED-CONTENT:START (CASES) --> | ||
## Tests | ||
|
||
### Invalid Examples | ||
|
||
These snippets cause lint errors, and some can be auto-fixed. | ||
|
||
```js | ||
let el = <div className="greeting">Hello world!</div>; | ||
// after eslint --fix: | ||
let el = <div class="greeting">Hello world!</div>; | ||
|
||
let el = <div className={"greeting"}>Hello world!</div>; | ||
// after eslint --fix: | ||
let el = <div class={"greeting"}>Hello world!</div>; | ||
|
||
let el = <div className="greeting" />; | ||
// after eslint --fix: | ||
let el = <div class="greeting" />; | ||
|
||
let el = ( | ||
<div many other attributes className="greeting"> | ||
Hello world! | ||
</div> | ||
); | ||
// after eslint --fix: | ||
let el = ( | ||
<div many other attributes class="greeting"> | ||
Hello world! | ||
</div> | ||
); | ||
|
||
let el = <PascalComponent className="greeting">Hello world!</PascalComponent>; | ||
// after eslint --fix: | ||
let el = <PascalComponent class="greeting">Hello world!</PascalComponent>; | ||
|
||
let el = <label htmlFor="id">Hello world!</label>; | ||
// after eslint --fix: | ||
let el = <label for="id">Hello world!</label>; | ||
|
||
let el = <label htmlFor={"id"}>Hello world!</label>; | ||
// after eslint --fix: | ||
let el = <label for={"id"}>Hello world!</label>; | ||
|
||
let el = ( | ||
<label many other attributes htmlFor="id"> | ||
Hello world! | ||
</label> | ||
); | ||
// after eslint --fix: | ||
let el = ( | ||
<label many other attributes for="id"> | ||
Hello world! | ||
</label> | ||
); | ||
|
||
let el = <PascalComponent htmlFor="id">Hello world!</PascalComponent>; | ||
// after eslint --fix: | ||
let el = <PascalComponent for="id">Hello world!</PascalComponent>; | ||
|
||
let el = <div key={item.id} />; | ||
// after eslint --fix: | ||
let el = <div />; | ||
|
||
``` | ||
|
||
### Valid Examples | ||
|
||
These snippets don't cause lint errors. | ||
|
||
```js | ||
let el = <div>Hello world!</div>; | ||
|
||
let el = <div class="greeting">Hello world!</div>; | ||
|
||
let el = <div class={"greeting"}>Hello world!</div>; | ||
|
||
let el = ( | ||
<div many other attributes class="greeting"> | ||
Hello world! | ||
</div> | ||
); | ||
|
||
let el = <label for="id">Hello world!</label>; | ||
|
||
let el = <label for="id">Hello world!</label>; | ||
|
||
let el = <label for={"id"}>Hello world!</label>; | ||
|
||
let el = ( | ||
<label many other attributes for="id"> | ||
Hello world! | ||
</label> | ||
); | ||
|
||
let el = <PascalComponent class="greeting" for="id" />; | ||
|
||
let el = <PascalComponent key={item.id} />; | ||
|
||
``` | ||
<!-- AUTO-GENERATED-CONTENT:END --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { TSESTree as T, TSESLint } from "@typescript-eslint/utils"; | ||
import { isFunctionNode, trackImports, isPropsByName, trace } from "../utils"; | ||
|
||
const rule: TSESLint.RuleModule< | ||
"noStore" | "spreadCall" | "spreadMember" | "proxyLiteral" | "mergeProps", | ||
[] | ||
> = { | ||
meta: { | ||
type: "problem", | ||
docs: { | ||
recommended: false, | ||
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.", | ||
}, | ||
}, | ||
create(context) { | ||
const { matchImport, handleImportDeclaration } = trackImports(); | ||
|
||
return { | ||
ImportDeclaration(node) { | ||
handleImportDeclaration(node); // track import aliases | ||
|
||
const source = node.source.value; | ||
if (source === "solid-js/store") { | ||
context.report({ | ||
node, | ||
messageId: "noStore", | ||
}); | ||
} | ||
}, | ||
"JSXSpreadAttribute MemberExpression"(node: T.MemberExpression) { | ||
context.report({ node, messageId: "spreadMember" }); | ||
}, | ||
"JSXSpreadAttribute CallExpression"(node: T.CallExpression) { | ||
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 = trace(arg, context.getScope()); | ||
return ( | ||
(traced.type === "Identifier" && !isPropsByName(traced.name)) || | ||
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" }); | ||
} | ||
}, | ||
}; | ||
}, | ||
}; | ||
|
||
export default rule; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { AST_NODE_TYPES as T } from "@typescript-eslint/utils"; | ||
import { run } from "../ruleTester"; | ||
import rule from "../../src/rules/no-proxy-apis"; | ||
|
||
// Don't bother checking for imports for every test | ||
jest.mock("../../src/utils", () => { | ||
return { | ||
...jest.requireActual("../../src/utils"), | ||
trackImports: () => { | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
const handleImportDeclaration = () => {}; | ||
const matchImport = (imports: string | Array<string>, str: string) => { | ||
const importArr = Array.isArray(imports) ? imports : [imports]; | ||
return importArr.find((i) => i === str); | ||
}; | ||
return { matchImport, handleImportDeclaration }; | ||
}, | ||
}; | ||
}); | ||
|
||
export const cases = run("no-proxy-apis", rule, { | ||
valid: [ | ||
`let merged = mergeProps({}, props);`, | ||
`const obj = {}; let merged = mergeProps(obj, props);`, | ||
`let obj = {}; let merged = mergeProps(obj, props);`, | ||
`let merged = mergeProps({ get asdf() { signal() } }, props);`, | ||
`let el = <div {...{ asdf: 'asdf' }} />`, | ||
`let el = <div {...asdf} />`, | ||
`let obj = { Proxy: 1 }`, | ||
], | ||
invalid: [ | ||
{ | ||
code: `let proxy = new Proxy(asdf, {});`, | ||
errors: [{ messageId: "proxyLiteral" }], | ||
}, | ||
{ | ||
code: `let proxy = Proxy.revocable(asdf, {});`, | ||
errors: [{ messageId: "proxyLiteral" }], | ||
}, | ||
{ | ||
code: `import {} from 'solid-js/store';`, | ||
errors: [{ messageId: "noStore", type: T.ImportDeclaration }], | ||
}, | ||
{ | ||
code: `let el = <div {...maybeSignal()} />`, | ||
errors: [{ messageId: "spreadCall" }], | ||
}, | ||
{ | ||
code: `let el = <div {...{ ...maybeSignal() }} />`, | ||
errors: [{ messageId: "spreadCall" }], | ||
}, | ||
{ | ||
code: `let el = <div {...maybeProps.foo} />`, | ||
errors: [{ messageId: "spreadMember" }], | ||
}, | ||
{ | ||
code: `let el = <div {...{ ...maybeProps.foo }} />`, | ||
errors: [{ messageId: "spreadMember" }], | ||
}, | ||
{ | ||
code: `let merged = mergeProps(maybeSignal)`, | ||
errors: [{ messageId: "mergeProps" }], | ||
}, | ||
{ | ||
code: `let func = () => ({}); let merged = mergeProps(func, props)`, | ||
errors: [{ messageId: "mergeProps" }], | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters