-
-
Notifications
You must be signed in to change notification settings - Fork 418
Add isolated-functions rule
#2701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Actually maybe I should re-think how {
'unicorn/isolated-functions': [
'error',
{
globals: [
'node', // globals.node from `globals` package
'browser', // globals.browser from `globals` package
{foobar: true}, // also allow this global
{abc: 'off'}, // disallow this global
'xyz', // error, this doesn't exist in the `globals` package
}
]
}The above also allows for @fisker @sindresorhus what do you think? |
Users should always have |
|
|
||
| const MESSAGE_ID_EXTERNALLY_SCOPED_VARIABLE = 'externally-scoped-variable'; | ||
| const messages = { | ||
| [MESSAGE_ID_EXTERNALLY_SCOPED_VARIABLE]: 'Variable {{name}} not defined in scope of isolated function. Function is isolated because: {{reason}}.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this reason necessary to show in the message? When user see "in scope of isolated function", they should realize that it's isolated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to leave the "isolated reason" alone, but we can keep the "global variable is not writable", it can be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More information is more useful? If someone is new to a project they may not know that this rule exists, that the function is isolated, or what the team's conventions are. It's just a bit more of a hint about why the rule applies. LLMs will also likely be better at addressing the problem based on an error message which includes the reason. What's the downside to including it?
|
I didn't see handling of ES builtin globals. I think it should be ignored unless |
I have tests for globals. valid: eslint-plugin-unicorn/test/isolated-functions.js Lines 149 to 186 in 9be0de2
invalid: eslint-plugin-unicorn/test/isolated-functions.js Lines 149 to 186 in 9be0de2
They come from languageOptions by default but can be overriden:
Are you saying you think more tests are needed? Edit: a8467d7 |
|
I'm talking about https://github.com/sindresorhus/globals/blob/fa8aaaeb9b203468a07e9c2dacec9ca48527aa1f/globals.json#L1195 |
|
Maybe we need change const allowedGlobals = options.globals ?? context.languageOptions.globals; to const allowedGlobals = {
...(globals[`es${eslintContext.languageOptions.ecmaVersion}`] ?? globals.builtins),
...(options.globals ?? context.languageOptions.globals)
}; |
|
@fisker i see - good idea, done. |
|
@fisker @sindresorhus ok for this to go in? I'd love to use this at work without using an unstable version of this plugin. |
|
I'll take a final look. |
docs/rules/isolated-functions.md
Outdated
| 'unicorn/isolated-functions': [ | ||
| 'error', | ||
| { | ||
| globals: {} // Empty object disallows all globals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| globals: {} // Empty object disallows all globals | |
| globals: {} // Empty object disallows all globals except language globals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should mention that language globals are always enabled; they need to be disabled explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think worth changing the option to
{
esGlobals: boolean,
eslintGlobals: boolean,
overrideGlobals: {
...
}
}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about I change globals to overrideGlobals and we can add the other ones if/when somebody asks for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I think the behaviour is less confusing now - before globals: {} could turn off everything in language options, and I don't think it was good for globals: undefined to be so dramatically different from globals: {}.
Now, overrideGlobals does just that - it overrides globals that otherwise are defined, so if you want to turn off globals you turn them off explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commit: d9836a0
fisker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything looks good, just some nit comments.
|
Done - left in a couple of console.log things to showcase how you can still pass variables in to isolated functions ( |
|
There is a question #2701 (comment) |
Fixes #2214
From docs:
Prevent usage of variables from outside the scope of isolated functions
💼 This rule is enabled in the ✅
recommendedconfig.Some functions need to be isolated from their surrounding scope due to execution context constraints. For example, functions passed to
makeSynchronous()are executed in a worker or subprocess and cannot access variables from outside their scope. This rule helps identify when functions are using external variables that may cause runtime errors.Common scenarios where functions must be isolated:
makeSynchronous()(executed in worker)Function.prototype.toString()By default, this rule allows global variables (like
console,fetch, etc.) in isolated functions, but prevents usage of variables from the surrounding scope.Examples
Options
Type:
objectfunctions
Type:
string[]Default:
['makeSynchronous']Array of function names that create isolated execution contexts. Functions passed as arguments to these functions will be considered isolated.
selectors
Type:
string[]Default:
[]Array of ESLint selectors to identify isolated functions. Useful for custom naming conventions or framework-specific patterns.
comments
Type:
string[]Default:
['@isolated']Array of comment strings that mark functions as isolated. Functions with JSDoc comments containing these strings will be considered isolated.
globals
Type:
boolean | string[]Default:
trueControls how global variables are handled:
false: Global variables are not allowed in isolated functionstrue(default): All globals from ESLint's language options are allowedstring[]: Only the specified global variable names are allowedExamples
Custom function names
Lambda function naming convention
Allowing specific globals
Note: I didn't go as far as suggested in that issue #2214 (comment) - that is, to track imports from specific modules like
import makeSync from 'make-synchronous', to allow for arbitrary aliasing from that package. Instead I went for fairly specific naming conventions. But I thought the rule still had value while relying on naming convention, so opening a pull request now since I don't know when I'll get some more time to spend on it. I think the module-import-tracking thing could go in later as a new feature, if there's enough interest in it.