Skip to content

Commit

Permalink
feat: add suggestions and fixes (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpoehnelt authored Dec 30, 2021
1 parent d1a9ce2 commit 3e5740a
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 41 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,24 @@ To use rules provided by the plugin, use the following:
}
```

Some rules are fixable with `eslint --fix`. For example the [place-fields](docs/rules/place-fields.md) rule.

```js
service.getDetails({place_id: 'foo'})
```
becomes

```js
service.getDetails({fields: /** TODO: Add necessary fields to the request */ [], place_id: 'foo'})
```

## Rules

| Rule | Description | Configurations | Type |
| ------------------------------------------------------------ | ---------------------------------- | ---------------- | ------------ |
| [no-api-keys](docs/rules/no-api-keys.md) | Keep API keys out of code. | ![recommended][] | ![suggest][] |
| [place-fields](docs/rules/place-fields.md) | Always use place fields. | ![recommended][] | ![suggest][] |
| [require-js-api-loader](docs/rules/require-js-api-loader.md) | Require @googlemaps/js-api-loader. | ![recommended][] | ![suggest][] |
| [require-js-api-loader](docs/rules/require-js-api-loader.md) | Require @googlemaps/js-api-loader. | ![recommended][] | ![fixable][] |

[recommended]: https://img.shields.io/badge/-recommended-lightgrey.svg
[suggest]: https://img.shields.io/badge/-suggest-yellow.svg
Expand Down
29 changes: 23 additions & 6 deletions docs/rules/place-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@

# place-fields

Use the `fields` option to limit the fields returned by the API and costs. Request to the Places API are billed by the fields that are returned. See [data-skus](https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#data-skus) for more details.
> **Note**: This rule is not exhaustive and ignores `Autocomplete.setFields()`.
Use the `fields` option to limit the fields returned by the API and costs. Requests to the Places API are billed by the fields that are returned. See [Places Data SKUs](https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#data-skus) for more details.

More information about fields for specific API calls can be found at the following links:

- [Place Details fields guidance](https://goo.gle/3H0TxxG)
- [Place Autocomplete fields guidance](https://goo.gle/3sp2XyS)

> **Note**: This rule is not exhaustive. For example, it ignores `Autocomplete.setFields()`.
📋 This rule is enabled in `plugin:googlemaps/recommended`.

🔧 The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

## Rule details

❌ Examples of **incorrect** code:
```js
const service = new google.maps.places.PlacesService();
const request = {place_id: 'foo'};
service.getDetails(request)
service.getDetails({place_id: 'foo'})

const service = new google.maps.places.PlacesService();
service.getDetails({})
const request = {place_id: 'foo'};
service.getDetails(request)

const service = new google.maps.places.PlacesService();
service.getDetails({...{bar: 'foo'}})
service.getDetails({...{place_id: 'foo'}})

const service = new google.maps.places.Autocomplete(null, {});
const service = new google.maps.places.Autocomplete(null);
Expand Down Expand Up @@ -47,6 +55,15 @@ service.getDetails(buildRequest())
const service = new google.maps.places.Autocomplete(null, {fields: ['place_id']});
```

🔧 Examples of code **fixed** by this rule:
```js
const service = new google.maps.places.PlacesService(); /**/ const service = new google.maps.places.PlacesService();
service.getDetails({place_id: 'foo'}) /**/ service.getDetails({fields: /** TODO: Add necessary fields to the request */ [], place_id: 'foo'})

const service = new google.maps.places.PlacesService(); /**/ const service = new google.maps.places.PlacesService();
service.getDetails({...{place_id: 'foo'}}) /**/ service.getDetails({fields: /** TODO: Add necessary fields to the request */ [], ...{place_id: 'foo'}})
```

## Resources

* [Rule source](/src/rules/place-fields.ts)
Expand Down
46 changes: 23 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"prepare": "tsc",
"format": "eslint --ext .js,.ts src --fix",
"pretest": "eslint --ext .js,.ts src",
"test": "jest"
"test": "jest --detectOpenHandles"
},
"dependencies": {
"@typescript-eslint/experimental-utils": "^5.2.0",
"@typescript-eslint/scope-manager": "^5.2.0"
"@typescript-eslint/experimental-utils": "^5.8.0",
"@typescript-eslint/scope-manager": "^5.8.0"
},
"devDependencies": {
"@types/eslint": "^8.2.1",
Expand Down
16 changes: 15 additions & 1 deletion src/rules/no-api-keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,21 @@ new RuleTester({
invalid: [
{
code: 'const apiKey = "AIza00000000000000000000000000000000000";',
errors: [{ messageId }],
errors: [
{
messageId,
suggestions: [
{
messageId: "replaceWithEnvVar",
output: "const apiKey = process.env.GOOGLE_MAPS_API_KEY;",
},
{
messageId: "replaceWithPlaceholder",
output: 'const apiKey = "YOUR_API_KEY";',
},
],
},
],
},
],
});
20 changes: 20 additions & 0 deletions src/rules/no-api-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export default createRule({
},
messages: {
[messageId]: "Avoid placing API keys in source code.",
replaceWithEnvVar: "Use environment variables instead.",
replaceWithPlaceholder: "Use placeholder `YOUR_API_KEY` instead.",
},
hasSuggestions: true,
schema: [],
type: "suggestion",
},
Expand All @@ -52,6 +55,23 @@ export default createRule({
context.report({
node,
messageId,
suggest: [
{
messageId: "replaceWithEnvVar",
fix: (fixer) => {
return fixer.replaceText(
node,
`process.env.GOOGLE_MAPS_API_KEY`
);
},
},
{
messageId: "replaceWithPlaceholder",
fix: (fixer) => {
return fixer.replaceText(node, `"YOUR_API_KEY"`);
},
},
],
});
}
},
Expand Down
12 changes: 8 additions & 4 deletions src/rules/place-fields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,23 @@ service.getDetails(buildRequest())`,
// getDetails
{
code: `const service = new google.maps.places.PlacesService();
const request = {place_id: 'foo'};
service.getDetails(request)`,
service.getDetails({place_id: 'foo'})`,
errors: [{ messageId }],
output: `const service = new google.maps.places.PlacesService();
service.getDetails({fields: /** TODO: Add necessary fields to the request */ [], place_id: 'foo'})`,
},
{
code: `const service = new google.maps.places.PlacesService();
service.getDetails({})`,
const request = {place_id: 'foo'};
service.getDetails(request)`,
errors: [{ messageId }],
},
{
code: `const service = new google.maps.places.PlacesService();
service.getDetails({...{bar: 'foo'}})`,
service.getDetails({...{place_id: 'foo'}})`,
errors: [{ messageId }],
output: `const service = new google.maps.places.PlacesService();
service.getDetails({fields: /** TODO: Add necessary fields to the request */ [], ...{place_id: 'foo'}})`,
},
// Autocomplete
{
Expand Down
27 changes: 24 additions & 3 deletions src/rules/place-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@
* limitations under the License.
*/

import { TSESTree } from "@typescript-eslint/experimental-utils";
import { TSESLint, TSESTree } from "@typescript-eslint/experimental-utils";
import { Reference } from "@typescript-eslint/scope-manager";
import { createRule, camelCased } from "../utils/rules";

export const messageId = camelCased(__filename);

const description = `Use the \`fields\` option to limit the fields returned by the API and costs. Request to the Places API are billed by the fields that are returned. See [data-skus](https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#data-skus) for more details.
> **Note**: This rule is not exhaustive and ignores \`Autocomplete.setFields()\`.`;
const description = `Use the \`fields\` option to limit the fields returned by the API and costs. Requests to the Places API are billed by the fields that are returned. See [Places Data SKUs](https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#data-skus) for more details.
More information about fields for specific API calls can be found at the following links:
- [Place Details fields guidance](https://goo.gle/3H0TxxG)
- [Place Autocomplete fields guidance](https://goo.gle/3sp2XyS)
> **Note**: This rule is not exhaustive. For example, it ignores \`Autocomplete.setFields()\`.`;

export default createRule({
name: __filename,
meta: {
Expand All @@ -35,6 +42,7 @@ export default createRule({
},
schema: [],
type: "suggestion",
fixable: "code",
},
defaultOptions: [],
create: (context) => {
Expand Down Expand Up @@ -94,6 +102,19 @@ export default createRule({
context.report({
messageId,
node: node.property,
fix:
requestArgument.type === "ObjectExpression"
? (fixer: TSESLint.RuleFixer) => {
return [
fixer.insertTextBefore(
context
.getSourceCode()
.getTokens(requestArgument)[1],
`fields: /** TODO: Add necessary fields to the request */ [], `
),
];
}
: null,
});
}
}
Expand Down

0 comments on commit 3e5740a

Please sign in to comment.