eslintrc: Disallow calling String/Array/Object methods introduced in ES6 or later
authorBartosz Dziewoński <matma.rex@gmail.com>
Fri, 28 Sep 2018 12:36:04 +0000 (14:36 +0200)
committerJames D. Forrester <jforrester@wikimedia.org>
Wed, 7 Nov 2018 15:32:40 +0000 (07:32 -0800)
This aims to disallow using the methods listed below, which are
supported by Chromium 69 but not supported by Internet Explorer 11.
Most of them were introduced in ECMAScript 6, but some are more recent
or experimental.

We keep breaking IE 11 support by using them. Recent examples include:
* mediawiki/core:
  Ic85063dfbbcf26a99d343845c9fb801f1888d750
* mediawiki/extensions/MultimediaViewer:
  I0954c42a37668b0eb46c3e864a2e13152a6dc68a
* mediawiki/extensions/UploadWizard:
  I70d37a8f0abcc96b5e39fa71a93cda6f3421c87c
* VisualEditor/VisualEditor:
  Ic1971549da37091c41a847229d18e95aff5c9952

This will unfortunately almost certainly cause false positives, but
hopefully not too many of them. Common false positives can be
disallowed more precisely using 'no-restricted-syntax' rather than
'no-restricted-properties'.

Disallowed methods:
    String.prototype:
        codePointAt
        endsWith
        includes
        normalize
        padEnd                    (ES 2017)
        padStart                  (ES 2017)
        repeat
        startsWith
        trimEnd                   (experimental/proposed)
        trimLeft                  (experimental/proposed)
        trimRight                 (experimental/proposed)
        trimStart                 (experimental/proposed)
    Array.prototype:
        copyWithin
        entries
        fill
        find
        findIndex
        flat                      (experimental/proposed)
        flatMap                   (experimental/proposed)
        includes                  (ES 2016)
        keys
        values
    String:
        fromCodePoint
        raw
    Array:
        from
        of
    Object:
        assign
        entries                   (ES 2017)
        getOwnPropertyDescriptors (ES 2017)
        getOwnPropertySymbols
        is
        values                    (ES 2017)

I compiled the list based on running the following code in each
browser's console, since there doesn't seem to be an authoritative
list anywhere on the Internet:

    console.log( Object.getOwnPropertyNames( String.prototype ) )
    console.log( Object.getOwnPropertyNames( Array.prototype ) )
    console.log( Object.getOwnPropertyNames( Object.prototype ) )
    console.log( Object.getOwnPropertyNames( String ) )
    console.log( Object.getOwnPropertyNames( Array ) )
    console.log( Object.getOwnPropertyNames( Object ) )

Change-Id: Ic44c79834f99c52863fbc544ad657787f70fb9bb

.eslintrc.json
tests/selenium/.eslintrc.json

index c076751..40e26ca 100644 (file)
                                "object": "$",
                                "property": "proxy",
                                "message": "Please use Function.prototype.bind instead"
+                       },
+
+                       {
+                               "property": "codePointAt",
+                               "message": "Unsupported method String.prototype.codePointAt requires ES6."
+                       },
+                       {
+                               "property": "endsWith",
+                               "message": "Unsupported method String.prototype.endsWith requires ES6."
+                       },
+                       {
+                               "property": "normalize",
+                               "message": "Unsupported method String.prototype.normalize requires ES6."
+                       },
+                       {
+                               "property": "padEnd",
+                               "message": "Unsupported method String.prototype.padEnd requires ES2017."
+                       },
+                       {
+                               "property": "padStart",
+                               "message": "Unsupported method String.prototype.padStart requires ES2017."
+                       },
+                       {
+                               "property": "repeat",
+                               "message": "Unsupported method String.prototype.repeat requires ES6."
+                       },
+                       {
+                               "property": "startsWith",
+                               "message": "Unsupported method String.prototype.startsWith requires ES6."
+                       },
+                       {
+                               "property": "trimEnd",
+                               "message": "Unsupported method String.prototype.trimEnd is still experimental."
+                       },
+                       {
+                               "property": "trimLeft",
+                               "message": "Unsupported method String.prototype.trimLeft is still experimental."
+                       },
+                       {
+                               "property": "trimRight",
+                               "message": "Unsupported method String.prototype.trimRight is still experimental."
+                       },
+                       {
+                               "property": "trimStart",
+                               "message": "Unsupported method String.prototype.trimStart is still experimental."
+                       },
+                       {
+                               "property": "copyWithin",
+                               "message": "Unsupported method Array.prototype.copyWithin requires ES6."
+                       },
+                       {
+                               "property": "entries",
+                               "message": "Unsupported method Array.prototype.entries requires ES6."
+                       },
+                       {
+                               "property": "fill",
+                               "message": "Unsupported method Array.prototype.fill requires ES6."
+                       },
+                       {
+                               "property": "findIndex",
+                               "message": "Unsupported method Array.prototype.findIndex requires ES6."
+                       },
+                       {
+                               "property": "flat",
+                               "message": "Unsupported method Array.prototype.flat is still experimental."
+                       },
+                       {
+                               "property": "flatMap",
+                               "message": "Unsupported method Array.prototype.flatMap is still experimental."
+                       },
+                       {
+                               "object": "String",
+                               "property": "fromCodePoint",
+                               "message": "Unsupported method String.fromCodePoint requires ES6."
+                       },
+                       {
+                               "object": "String",
+                               "property": "raw",
+                               "message": "Unsupported method String.raw requires ES6."
+                       },
+                       {
+                               "object": "Array",
+                               "property": "from",
+                               "message": "Unsupported method Array.from requires ES6."
+                       },
+                       {
+                               "object": "Array",
+                               "property": "of",
+                               "message": "Unsupported method Array.of requires ES6."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "assign",
+                               "message": "Unsupported method Object.assign requires ES6."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "entries",
+                               "message": "Unsupported method Object.entries requires ES2017."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "getOwnPropertyDescriptors",
+                               "message": "Unsupported method Object.getOwnPropertyDescriptors requires ES2017."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "getOwnPropertySymbols",
+                               "message": "Unsupported method Object.getOwnPropertySymbols requires ES6."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "is",
+                               "message": "Unsupported method Object.is requires ES6."
+                       },
+                       {
+                               "object": "Object",
+                               "property": "values",
+                               "message": "Unsupported method Object.values requires ES2017."
+                       }
+               ],
+               "no-restricted-syntax": [
+                       2,
+                       {
+                               // Match expressions like .includes( … ) (false positives when used as a property name)
+                               "selector": "CallExpression[callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name='includes']",
+                               "message": "Unsupported methods String.prototype.includes and Array.prototype.includes require ES6 and ES2016 respectively."
+                       },
+                       {
+                               // Match expressions like .find( function ( … ) { … } ) (avoid $( … ).find( … ))
+                               "selector": "CallExpression[callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name='find'][arguments.length=1][arguments.0.type='FunctionExpression']",
+                               "message": "Unsupported method Array.prototype.find requires ES6."
+                       },
+                       {
+                               // Match expressions like .keys( … ), except Object.keys( … ) (which is valid)
+                               "selector": "CallExpression[callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name='keys'][callee.object.name!='Object']",
+                               "message": "Unsupported method Array.prototype.keys requires ES6."
+                       },
+                       {
+                               // Match expressions like .values( … ), except Object.values( … ) (disallowed separately)
+                               "selector": "CallExpression[callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name='values'][callee.object.name!='Object']",
+                               "message": "Unsupported method Array.prototype.values requires ES6."
                        }
                ],
                "dot-notation": 0,
index e39226c..ad4c97b 100644 (file)
@@ -9,6 +9,7 @@
                "browser": false
        },
        "rules":{
-               "no-console": 0
+               "no-console": 0,
+               "no-restricted-properties": 0
        }
 }