getify / eslint-plugin-proper-arrows

ESLint rules to ensure proper arrow function definitions
MIT License
305 stars 14 forks source link

Allowing no name for class property functions? #15

Closed natehardy closed 5 years ago

natehardy commented 5 years ago

At the moment, the plugin is not allowing class property functions. Is this intended? Would it be useful to have an option to allow this inside the no-name rule?

getify commented 5 years ago

Can you give me a code example?

getify commented 5 years ago

FWIW, I used this code example (hoping this is what you meant):

class Foo {
   fn = x => this.x = x;

   constructor() {
      console.log(this.fn.name);
   }
}

var f = new Foo();
// "fn"

IOW, class properties (of function expressions) should have name inference, so I just added that handling. No need for extra options, should just automatically work now. Released as v7.0.0.

Thanks for catching it. Technically class-properties are still just stage3 so not yet official in JS, but they're pretty common, and pretty certain to land, so... worth supporting. :)

natehardy commented 5 years ago

Sorry I didn't send an example. I Didn't get notified of the message. I updated the plugin and it is still throwing errors in my react classes. I thought it might be related to React so I pasted your code example from above into my code and also got an error for no name. The console is also not printing "fn". Is there something I'm missing about the name property on classes?

getify commented 5 years ago

I need to see a code example or I can't just guess at what code is causing you problems?


But if you took my above code and ran it with the current version of the plugin, and it complained about the name missing, then there's definitely something wrong/strange with your setup.

This is running that code in Chrome console:

Screen Shot 2019-03-27 at 3 01 41 PM

As you can see, the name inference works fine.

Perhaps you're running some sort of setup that is converting the code to something not actually using class before it's handing the code to eslint?

natehardy commented 5 years ago

Thanks so much for responding. I know you're very busy so ignore if you don't have time but the following are the relevant pieces:

Using "@getify/eslint-plugin-proper-arrows": "7.1.0"

webpack config const Dotenv = require("dotenv-webpack"); const { resolve } = require("path"); const webpack = require("webpack"); const ModernizrWebpackPlugin = require("modernizr-webpack-plugin"); const modernizrConfig = require("../.modernizrrc"); /* baseline commons.bundle.js (4.75 MiB) main.bundle.js (428 KiB) */ module.exports = function(env = {}) { const bundleMap = env.dev ? { main: ["@babel/polyfill", "react-hot-loader/patch", "./src/main.js"], admin: ["@babel/polyfill", "react-hot-loader/patch", "./src/admin.js"] } : { main: ["@babel/polyfill", "./src/main.js"], admin: ["@babel/polyfill", "./src/admin.js"] }; const clientName = env.client_name; const authType = env.auth_type || "form-login"; return { entry: bundleMap, stats: { colors: true, errors: true, errorDetails: true }, output: { filename: "[name].bundle.js", chunkFilename: "[name].bundle.js", path: resolve(__dirname, "../dist"), publicPath: 'https://localhost:8443/app/dist/', sourceMapFilename: "[name].bundle.js.map" }, devtool: "eval", optimization: { splitChunks: { cacheGroups: { commons: { name: "commons", chunks: "initial", minChunks: 2 } } } }, module: { rules: [ /*{ test: /\.js$/, use: [ 'ify-loader', 'transform-loader?plotly.js/tasks/compress_attributes.js', ] },*/ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { importLoaders: 1 } }, { loader: "postcss-loader", options: { plugins: function() { return [ require("postcss-sprites"), require("autoprefixer") ]; } } } ] }, { test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, use: { loader: "url-loader", options: { limit: 5000 } } } ] }, resolve: { extensions: [".js"], modules: [__dirname, resolve(__dirname, "../node_modules")], alias: { api: resolve(__dirname, "../src/api/"), data: resolve(__dirname, "../src/data/"), components: resolve(__dirname, "../src/components/"), wrapper: resolve(__dirname, "../src/components/wrapper"), form: resolve(__dirname, "../src/components/form"), grid: resolve(__dirname, "../src/components/grid/"), lib: resolve(__dirname, "../src/lib/"), resources: resolve(__dirname, "../src/resources/"), store: resolve(__dirname, "../src/store/"), routes: resolve(__dirname, "../src/routes/"), locations: resolve(__dirname, "../src/routes/locations"), ThemeProvider: resolve( __dirname, "../src/components/ThemeProvider" ), "images": resolve(__dirname, "../../images/"), "ag-grid": resolve("./node_modules/ag-grid") } }, plugins: [ new Dotenv({ path: `./config/client/${env.client_name}.env` // load this now instead of the ones in '.env' //safe: true, // load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file. //systemvars: true, // load all the predefined 'process.env' variables which will trump anything local per dotenv specs. //silent: true // hide any errors }), //new webpack.optimize.ModuleConcatenationPlugin(), new ModernizrWebpackPlugin(modernizrConfig), new webpack.IgnorePlugin(/vertx/), // When needing a custom file for a client new webpack.NormalModuleReplacementPlugin( /(.*)-CLIENT_NAME(\.*)/, function(resource) { resource.request = resource.request.replace( /-CLIENT_NAME/, clientName ? `-${clientName}` : `` ); } ), new webpack.DefinePlugin({ __IN_DEVELOPMENT__: JSON.stringify(env.dev), __IN_PRODUCTION__: JSON.stringify(env.production), __CLIENT_NAME__: JSON.stringify(clientName), __AUTH_TYPE__: JSON.stringify(authType), }) ] }; };

Console output in chrome from calling the TestClass function: image

Screenshot from intellij (could be a intellij anamoly) noname

If you have time, would love yo know your thoughts. Thanks, Nate

getify commented 5 years ago

Your code works fine in my eslint setup, using the "babel-eslint" parser -- which is necessary because the feature class-properties is stage3 (not official JS) and thus doesn't work in the standard eslint parser.

Screen Shot 2019-03-28 at 10 56 46 AM

You'll notice there, I have the same arrow function expression used two different kinds of ways, once as a call-argument (no name inference possible) and once as a class property. My linter only gives me the name-inference linting error for the former, not the latter.

I only have two theories to explain why you're seeing this, but they're not great theories.

  1. You think you're using the updated "proper-arrows" plugin, but somehow your setup is using the older/cached version. I don't know how to verify this, per se, but it would explain why you're still getting the error.

    However, that doesn't explain why, in your setup, the name inference itself actually fails, and you get an empty printout for name. Chrome is doing the correct thing, which is that there should be a name inference there. So we know your setup is somehow doing less than the correct/accurate thing with the JS you're writing.

  2. Something about your setup is using a different kind of parser/transpilation which is giving eslint a different version of your code than what's actually displayed on your screen. I didn't see anything obvious about that in your webpack config, but I'm not very knowledgable about webpack so I don't know what most of it means, TBH.

What I do know is, the transpiled version of that class property setting, via Babel (with their stage3 + spec setting, as well as the class properties transform plugin), looks basically like this:

 _defineProperty(_assertThisInitialized(_this), "myFunc", function (abc, def) {
   _newArrowCheck(this, _this2);
   return _this.whatever(abc, def);
 }.bind(this));

Notice the anonymous function on that first line? Babel appears to not be trying to do anything to preserve the name inferencing that is supposed to occur. They should be doing function myFunc(abc,def) { .. to preserve the name.

That's strange that they're not doing it, because they do handle the name inferencing for code like this:

var fn = () => { console.log(fn.name); };

Babel turns that code into:

var _fn = function fn() {
  _newArrowCheck(this, _this);
  console.log(_fn.name);
}.bind(void 0);

Notice there, they used function fn() { ...

However, in both cases, they're overriding the name inferencing anyway, by using .bind(..), which ends up making the name print out "bound .." instead. So it's all moot. That's pretty annoying, but I guess babel doesn't really care that much about spec-correct behavior.


Boiling all this evidence/obvservation down, I still don't have an explanation, because neither theory really fully explains what you're seeing.

natehardy commented 5 years ago

So much for spending the time to look into this. I know how busy you are. If I figure out what's going on, I'll let you know. As an aside, your JS workshops have really deepened my working knowledge and understanding of JS and I really appreciate all the clarity you bring to explaining how to become a better coder. Thanks!

natehardy commented 5 years ago

I deleted the node-modules directory and re-installed all the dependencies and the error is no longer appearing. It must have been some conflict with the previous version not being removed properly. Thanks again for all your help.

getify commented 5 years ago

Excellent, glad to hear that! :)

natehardy commented 5 years ago

Interesting side note - the console is still logging a blank name for the function name so it must be a transform that webpack is doing or a plugin I have in webpack. Thanks again.