Skip to content

Commit 55036f1

Browse files
committed
exts
1 parent 4fbb759 commit 55036f1

File tree

3 files changed

+29
-22
lines changed

3 files changed

+29
-22
lines changed

docs/rules/isolated-functions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
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 subprocess and cannot access variables from outside their scope. This rule helps identify when functions are using external variables that may cause runtime errors.
99

1010
Common scenarios where functions must be isolated:
11+
1112
- Functions passed to `makeSynchronous()` (executed in subprocess)
1213
- Functions that will be serialized via `Function.prototype.toString()`
1314
- Server actions or other remote execution contexts
@@ -213,4 +214,4 @@ makeSynchronous(async () => {
213214
const url = new URL(response.url); // ✅ Allowed global
214215
return response.text();
215216
});
216-
```
217+
```

rules/isolated-functions.js

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,17 @@ const parseEsquerySelector = selector => {
1616
return parsedEsquerySelectors.get(selector);
1717
};
1818

19-
/** @type {{functions: string[], selectors: string[], comments: string[], globals: boolean | string[]}} */
20-
const defaultOptions = {
21-
functions: ['makeSynchronous'],
22-
selectors: [],
23-
comments: ['@isolated'],
24-
globals: false,
25-
};
26-
2719
/** @param {import('eslint').Rule.RuleContext} context */
2820
const create = context => {
2921
const {sourceCode} = context;
30-
/** @type {typeof defaultOptions} */
22+
/** @type {{functions: string[], selectors: string[], comments: string[], globals: boolean | string[]}} */
3123
const userOptions = context.options[0];
24+
/** @type {typeof userOptions} */
3225
const options = {
33-
...defaultOptions,
26+
functions: ['makeSynchronous'],
27+
selectors: [],
28+
comments: ['@isolated'],
29+
globals: false,
3430
...userOptions,
3531
};
3632

@@ -43,7 +39,7 @@ const create = context => {
4339

4440
/** @param {import('estree').Node} node */
4541
const checkForExternallyScopedVariables = node => {
46-
const reason = reasaonForBeingIsolatedFunction(node);
42+
const reason = reasonForBeingIsolatedFunction(node);
4743
if (!reason) {
4844
return;
4945
}
@@ -68,17 +64,20 @@ const create = context => {
6864
};
6965

7066
/** @param {import('estree').Node & {parent?: import('estree').Node}} node */
71-
const reasaonForBeingIsolatedFunction = node => {
67+
const reasonForBeingIsolatedFunction = node => {
7268
if (options.comments.length > 0) {
7369
let previousToken = sourceCode.getTokenBefore(node, {includeComments: true});
7470
let commentableNode = node;
75-
while (previousToken?.type !== 'Block' && (commentableNode.parent.type === 'VariableDeclarator' || commentableNode.parent.type === 'VariableDeclaration')) {
71+
while (
72+
(previousToken?.type !== 'Block' && previousToken?.type !== 'Line')
73+
&& (commentableNode.parent.type === 'VariableDeclarator' || commentableNode.parent.type === 'VariableDeclaration')
74+
) {
7675
// Search up to find jsdoc comments on the parent declaration `/** @isolated */ const foo = () => abc`
7776
commentableNode = commentableNode.parent;
7877
previousToken = sourceCode.getTokenBefore(commentableNode, {includeComments: true});
7978
}
8079

81-
if (previousToken?.type === 'Block') {
80+
if (previousToken?.type === 'Block' || previousToken?.type === 'Line') {
8281
const previousComment = previousToken.value.trim().toLowerCase();
8382
const match = options.comments.find(comment => previousComment.includes(comment));
8483
if (match) {
@@ -104,8 +103,6 @@ const create = context => {
104103
return `matches selector ${JSON.stringify(matchedSelector)}`;
105104
}
106105
}
107-
108-
109106
};
110107

111108
return Object.fromEntries(functionTypes.map(type => [

test/isolated-functions.mjs renamed to test/isolated-functions.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import stripIndent from 'strip-indent';
2-
import {getTester} from './utils/test.mjs';
2+
import {getTester} from './utils/test.js';
33

44
const {test} = getTester(import.meta);
55

@@ -85,9 +85,18 @@ test({
8585
`),
8686
errors: [error({name: 'foo', reason: 'follows comment containing "@isolated"'})],
8787
},
88+
{
89+
name: '@isolated inline comment',
90+
code: stripIndent(`
91+
const foo = 'hi';
92+
// @isolated
93+
const abc = () => foo.slice();
94+
`),
95+
errors: [error({name: 'foo', reason: 'follows comment containing "@isolated"'})],
96+
},
8897
{
8998
name: 'global variables not allowed by default',
90-
globals: {foo: true},
99+
languageOptions: {globals: {foo: true}},
91100
code: stripIndent(`
92101
makeSynchronous(function () {
93102
return foo.slice();
@@ -97,7 +106,7 @@ test({
97106
},
98107
{
99108
name: 'global variables can be explicitly disallowed',
100-
globals: {foo: true},
109+
languageOptions: {globals: {foo: true}},
101110
options: [{globals: false}],
102111
code: stripIndent(`
103112
makeSynchronous(function () {
@@ -171,7 +180,7 @@ test({
171180
},
172181
{
173182
name: 'can allow global variables from language options',
174-
globals: {foo: true},
183+
languageOptions: {globals: {foo: true}},
175184
options: [{globals: true}],
176185
code: stripIndent(`
177186
makeSynchronous(function () {
@@ -181,7 +190,7 @@ test({
181190
},
182191
{
183192
name: 'allow global variables separate from language options',
184-
globals: {abc: true},
193+
languageOptions: {globals: {abc: true}},
185194
options: [{globals: ['foo']}],
186195
code: stripIndent(`
187196
makeSynchronous(function () {

0 commit comments

Comments
 (0)