Skip to content

Commit bb3be6e

Browse files
authoredMar 24, 2019
support detecting locally defined polyfills (#207)
* support detecting locally defined polyfills * handle commonjs imports
1 parent 6373ab5 commit bb3be6e

File tree

4 files changed

+170
-7
lines changed

4 files changed

+170
-7
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ eslint-plugin-compat
44
[![NPM version](https://badge.fury.io/js/eslint-plugin-compat.svg)](http://badge.fury.io/js/eslint-plugin-compat)
55
[![Dependency Status](https://img.shields.io/david/amilajack/eslint-plugin-compat.svg)](https://david-dm.org/amilajack/eslint-plugin-compat)
66
[![npm](https://img.shields.io/npm/dm/eslint-plugin-compat.svg)](https://npm-stat.com/charts.html?package=eslint-plugin-compat)
7+
[![Backers on Open Collective](https://opencollective.com/eslint-plugin-compat/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/eslint-plugin-compat/sponsors/badge.svg)](#sponsors)
78

89
Lint the browser compatibility of your code
910

‎package.json

+5
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,10 @@
7171
"engines": {
7272
"node": ">=8.x",
7373
"npm": ">=6.8.0"
74+
},
75+
"collective": {
76+
"type": "opencollective",
77+
"url": "https://opencollective.com/eslint-plugin-compat",
78+
"logo": "https://opencollective.com/opencollective/logo.txt"
7479
}
7580
}

‎src/rules/compat.js

+53-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ type Context = {
1717
report: () => void
1818
};
1919

20+
function getName(node) {
21+
switch (node.type) {
22+
case 'NewExpression': {
23+
return node.callee.name;
24+
}
25+
case 'MemberExpression': {
26+
return node.object.name;
27+
}
28+
case 'CallExpression': {
29+
return node.callee.name;
30+
}
31+
default:
32+
throw new Error('not found');
33+
}
34+
}
35+
2036
export type BrowserListConfig =
2137
| Array<string>
2238
| {
@@ -47,6 +63,8 @@ export default {
4763
DetermineTargetsFromConfig(browserslistConfig)
4864
);
4965

66+
const errors = [];
67+
5068
function lint(node: ESLintNode) {
5169
const { isValid, rule, unsupportedTargets } = Lint(
5270
node,
@@ -55,7 +73,7 @@ export default {
5573
);
5674

5775
if (!isValid) {
58-
context.report({
76+
errors.push({
5977
node,
6078
message: [
6179
generateErrorName(rule),
@@ -66,14 +84,43 @@ export default {
6684
}
6785
}
6886

87+
const identifiers = new Set();
88+
6989
return {
70-
// HACK: Ideally, rules will be generated at runtime. Each rule will have
71-
// have the ability to register itself to run on specific AST
72-
// nodes. For now, we're using the `CallExpression` node since
73-
// its what most rules will run on
7490
CallExpression: lint,
7591
MemberExpression: lint,
76-
NewExpression: lint
92+
NewExpression: lint,
93+
// Keep track of all the defined variables. Do not report errors for nodes that are not defined
94+
Identifier(node) {
95+
if (node.parent) {
96+
const { type } = node.parent;
97+
if (
98+
// ex. const { Set } = require('immutable');
99+
type === 'Property' ||
100+
// ex. function Set() {}
101+
type === 'FunctionDeclaration' ||
102+
// ex. const Set = () => {}
103+
type === 'VariableDeclarator' ||
104+
// ex. class Set {}
105+
type === 'ClassDeclaration' ||
106+
// ex. import Set from 'set';
107+
type === 'ImportDefaultSpecifier' ||
108+
// ex. import {Set} from 'set';
109+
type === 'ImportSpecifier' ||
110+
// ex. import {Set} from 'set';
111+
type === 'ImportDeclaration'
112+
) {
113+
identifiers.add(node.name);
114+
}
115+
}
116+
},
117+
'Program:exit': () => {
118+
// Get a map of all the variables defined in the root scope (not the global scope)
119+
// const variablesMap = context.getScope().childScopes.map(e => e.set)[0];
120+
errors
121+
.filter(error => !identifiers.has(getName(error.node)))
122+
.forEach(node => context.report(node));
123+
}
77124
};
78125
}
79126
};

‎test/e2e.spec.js

+111-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,98 @@ import { RuleTester } from 'eslint';
33
import rule from '../src/rules/compat';
44

55
const ruleTester = new RuleTester({
6-
parserOptions: { ecmaVersion: 2015 }
6+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' }
77
});
88

99
ruleTester.run('compat', rule, {
1010
valid: [
11+
{
12+
code: `
13+
import { Set } from 'immutable';
14+
new Set();
15+
`,
16+
settings: { browsers: ['ie 9'] }
17+
},
18+
{
19+
code: `
20+
const { Set } = require('immutable');
21+
new Set();
22+
`,
23+
settings: { browsers: ['ie 9'] }
24+
},
25+
{
26+
code: `
27+
const Set = require('immutable').Set;
28+
new Set();
29+
`,
30+
settings: { browsers: ['ie 9'] }
31+
},
32+
{
33+
code: `
34+
const { Set } = require('immutable');
35+
(() => {
36+
new Set();
37+
})();
38+
`,
39+
settings: { browsers: ['ie 9'] }
40+
},
41+
{
42+
code: `
43+
import Set from 'immutable';
44+
new Set();
45+
`,
46+
settings: { browsers: ['ie 9'] }
47+
},
48+
{
49+
code: `
50+
function Set() {}
51+
new Set();
52+
`,
53+
settings: { browsers: ['ie 9'] }
54+
},
55+
{
56+
code: `
57+
const Set = () => {};
58+
new Set();
59+
`,
60+
settings: { browsers: ['ie 9'] }
61+
},
62+
{
63+
code: `
64+
const bar = () => {
65+
const Set = () => {};
66+
new Set();
67+
}
68+
`,
69+
settings: { browsers: ['ie 9'] }
70+
},
71+
{
72+
code: `
73+
const bar = () => {
74+
class Set {}
75+
new Set()
76+
}
77+
`,
78+
settings: { browsers: ['ie 9'] }
79+
},
80+
{
81+
code: `
82+
const bar = () => {
83+
const Set = {}
84+
new Set()
85+
}
86+
`,
87+
settings: { browsers: ['ie 9'] }
88+
},
89+
{
90+
code: `
91+
const bar = () => {
92+
function Set() {}
93+
new Set()
94+
}
95+
`,
96+
settings: { browsers: ['ie 9'] }
97+
},
1198
{
1299
code: 'document.documentElement()',
13100
settings: { browsers: ['Safari 11', 'Opera 57', 'Edge 17'] }
@@ -70,6 +157,29 @@ ruleTester.run('compat', rule, {
70157
}
71158
],
72159
invalid: [
160+
{
161+
code: `
162+
import { Map } from 'immutable';
163+
new Set()
164+
`,
165+
settings: { browsers: ['ie 9'] },
166+
errors: [
167+
{
168+
message: 'Set is not supported in IE 9',
169+
type: 'NewExpression'
170+
}
171+
]
172+
},
173+
{
174+
code: 'new Set()',
175+
settings: { browsers: ['ie 9'] },
176+
errors: [
177+
{
178+
message: 'Set is not supported in IE 9',
179+
type: 'NewExpression'
180+
}
181+
]
182+
},
73183
{
74184
code: 'new TypedArray()',
75185
settings: { browsers: ['ie 9'] },

0 commit comments

Comments
 (0)
Please sign in to comment.