Skip to content

Commit b4f5759

Browse files
committed
Update the no-unstable-useOnyx-defaults rule
1 parent 99ca082 commit b4f5759

4 files changed

Lines changed: 134 additions & 71 deletions

File tree

eslint-plugin-expensify/no-empty-array-object-default-in-useOnyx.js

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @typedef {import('eslint').Rule.RuleModule} RuleModule
3+
*/
4+
5+
/** @type {RuleModule} */
6+
module.exports = {
7+
name: 'no-unstable-useOnyx-defaults',
8+
meta: {
9+
type: 'problem',
10+
docs: {
11+
description: 'Disallow inline array or object literals as default values in useOnyx. Use getEmptyArray(), getEmptyObject(), a constant, or a memoized value instead.',
12+
recommended: 'error',
13+
},
14+
schema: [],
15+
messages: {
16+
noEmptyArrayDefault: 'Avoid using empty array [] as default value in useOnyx. Use getEmptyArray<T>() instead.',
17+
noInlineArrayDefault: 'Avoid using array literal as default value in useOnyx. Use a memoized value or a constant instead.',
18+
noEmptyObjectDefault: 'Avoid using empty object {} as default value in useOnyx. Use getEmptyObject<T>() instead.',
19+
noInlineObjectDefault: 'Avoid using object literal as default value in useOnyx. Use a memoized value or a constant instead.',
20+
noUnstableIdentifierDefault: 'Default value must be memoized with useMemo or declared as a top-level constant.',
21+
},
22+
},
23+
create(context) {
24+
return {
25+
VariableDeclarator(node) {
26+
if (
27+
!node.init
28+
|| node.init.type !== 'CallExpression'
29+
|| node.init.callee.name !== 'useOnyx'
30+
) {
31+
return;
32+
}
33+
34+
// Check if the variable declaration is an array pattern (destructuring)
35+
if (node.id.type !== 'ArrayPattern') {
36+
return;
37+
}
38+
39+
node.id.elements.forEach((element) => {
40+
if (!element || element.type !== 'AssignmentPattern') {
41+
return;
42+
}
43+
44+
const defaultValue = element.right;
45+
46+
// Check for inline array default
47+
if (defaultValue.type === 'ArrayExpression') {
48+
const isEmpty = defaultValue.elements.length === 0;
49+
context.report({
50+
node: defaultValue,
51+
messageId: isEmpty ? 'noEmptyArrayDefault' : 'noInlineArrayDefault',
52+
});
53+
}
54+
55+
// Check for inline object default
56+
if (defaultValue.type === 'ObjectExpression') {
57+
const isEmpty = defaultValue.properties.length === 0;
58+
context.report({
59+
node: defaultValue,
60+
messageId: isEmpty ? 'noEmptyObjectDefault' : 'noInlineObjectDefault',
61+
});
62+
}
63+
64+
// If it's an Identifier, verify it's memoized or top-level const
65+
if (defaultValue.type === 'Identifier') {
66+
const scope = context.getScope();
67+
const variable = scope.set.get(defaultValue.name);
68+
69+
if (
70+
variable
71+
&& variable.defs.length > 0
72+
&& variable.defs[0].node.type === 'VariableDeclarator'
73+
) {
74+
const def = variable.defs[0].node;
75+
const init = def.init;
76+
77+
const isMemoized = init
78+
&& init.type === 'CallExpression'
79+
&& init.callee.type === 'Identifier'
80+
&& init.callee.name === 'useMemo';
81+
82+
const isTopLevelConst = variable.scope.type === 'module'
83+
&& variable.defs[0].parent.kind === 'const';
84+
85+
if (!isMemoized && !isTopLevelConst) {
86+
context.report({
87+
node: defaultValue,
88+
messageId: 'noUnstableIdentifierDefault',
89+
});
90+
}
91+
}
92+
}
93+
});
94+
},
95+
};
96+
},
97+
};

eslint-plugin-expensify/tests/no-empty-array-object-default-in-useOnyx.test.js renamed to eslint-plugin-expensify/tests/no-unstable-useOnyx-defaults.test.js

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const RuleTester = require('eslint').RuleTester;
2-
const rule = require('../no-empty-array-object-default-in-useOnyx');
2+
const rule = require('../no-unstable-useOnyx-defaults');
33

44
const ruleTester = new RuleTester({
55
parserOptions: {
@@ -8,7 +8,7 @@ const ruleTester = new RuleTester({
88
},
99
});
1010

11-
ruleTester.run('no-empty-array-object-default-in-useOnyx', rule, {
11+
ruleTester.run('no-unstable-useOnyx-defaults', rule, {
1212
valid: [
1313
{
1414
code: 'const [data = getEmptyObject()] = useOnyx(ONYXKEYS.DATA);',
@@ -35,16 +35,23 @@ ruleTester.run('no-empty-array-object-default-in-useOnyx', rule, {
3535
code: 'const [data = 0] = useOnyx(ONYXKEYS.DATA);',
3636
},
3737
{
38-
code: 'const [data = someVariable] = useOnyx(ONYXKEYS.DATA);',
38+
code: 'const data = useOnyx(ONYXKEYS.DATA);',
3939
},
4040
{
41-
code: 'const [data = {key: "value"}] = useOnyx(ONYXKEYS.DATA);',
41+
code: `
42+
const CONST_VALUE = {key: "value"};
43+
function Component() {
44+
const [data = CONST_VALUE] = useOnyx(ONYXKEYS.DATA);
45+
}
46+
`,
4247
},
4348
{
44-
code: 'const [data = ["item"]] = useOnyx(ONYXKEYS.DATA);',
45-
},
46-
{
47-
code: 'const data = useOnyx(ONYXKEYS.DATA);',
49+
code: `
50+
function Component() {
51+
const memoizedValue = useMemo(() => ({key: "value"}), []);
52+
const [data = memoizedValue] = useOnyx(ONYXKEYS.DATA);
53+
}
54+
`,
4855
},
4956
],
5057
invalid: [
@@ -60,5 +67,26 @@ ruleTester.run('no-empty-array-object-default-in-useOnyx', rule, {
6067
messageId: 'noEmptyArrayDefault',
6168
}],
6269
},
70+
{
71+
code: 'const [data = {key: "value"}] = useOnyx(ONYXKEYS.DATA);',
72+
errors: [{
73+
messageId: 'noInlineObjectDefault',
74+
}],
75+
},
76+
{
77+
code: 'const [data = ["item"]] = useOnyx(ONYXKEYS.DATA);',
78+
errors: [{
79+
messageId: 'noInlineArrayDefault',
80+
}],
81+
},
82+
{
83+
code: `
84+
function Component() {
85+
const notMemoizedValue = {key: "value"};
86+
const [data = notMemoizedValue] = useOnyx(ONYXKEYS.DATA);
87+
}
88+
`,
89+
errors: [{messageId: 'noUnstableIdentifierDefault'}],
90+
},
6391
],
6492
});

rules/expensify.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ module.exports = {
3131
}],
3232
'rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth': 'warn',
3333
'rulesdir/no-use-state-initializer-functions': 'error',
34-
'rulesdir/no-empty-array-object-default-in-useOnyx': 'error',
34+
'rulesdir/no-unstable-useOnyx-defaults': 'error',
3535
},
3636
};
3737

0 commit comments

Comments
 (0)