Skip to content

Commit 2944830

Browse files
committed
feat(eslint): track missing signals in serializer update()
1 parent 4d056d4 commit 2944830

File tree

10 files changed

+517
-7
lines changed

10 files changed

+517
-7
lines changed

packages/docs/src/routes/docs/(qwik)/advanced/eslint/index.mdx

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ contributors:
55
- manucorporat
66
- gioboa
77
- maiieul
8-
updated_at: '2023-11-22T07:02:07Z'
8+
- wmertens
9+
updated_at: '2025-03-18T00:00:00Z'
910
created_at: '2023-05-20T00:04:28Z'
1011
---
1112

@@ -15,6 +16,7 @@ created_at: '2023-05-20T00:04:28Z'
1516
[//]: <> ( to update run: pnpm eslint.update )
1617
[//]: <> ( after changing the rule metadata on )
1718
[//]: <> ( packages/eslint-plugin-qwik/index.ts )
19+
[//]: <> ( and update the frontmatter if needed )
1820
[//]: <> (--------------------------------------)
1921

2022
import './styles.css';
@@ -323,7 +325,33 @@ import './styles.css';
323325
</span>
324326
<span
325327
class={{
326-
'icon': true,
328+
'icon': false,
329+
'icon icon-inactive': true,
330+
}}
331+
>
332+
🔔
333+
</span>
334+
</div>
335+
</a>
336+
337+
338+
<a href="#serializer-signal-usage" class="p-4 flex panel">
339+
<div class="flex-1">
340+
<code>serializer-signal-usage</code>
341+
<span class="rule-description">Ensure signals used in update() are also used in deserialize()</span>
342+
</div>
343+
<div class="flex gap-2 items-center">
344+
<span
345+
class={{
346+
'icon': false,
347+
'icon icon-inactive': false,
348+
}}
349+
>
350+
351+
</span>
352+
<span
353+
class={{
354+
'icon': false,
327355
'icon icon-inactive': false,
328356
}}
329357
>
@@ -972,7 +1000,7 @@ export const ColorList = component$(() => {
9721000
<div class="code-wrapper">
9731001
<span class="badge good">✓</span>
9741002
```tsx {4,12} /serverGreeter/#a
975-
import { $, component$ } from '@qwik.dev/core';
1003+
import { component$ } from '@qwik.dev/core';
9761004
import { server$ } from '@qwik.dev/router';
9771005

9781006
const serverGreeter = server$((firstName: string, lastName: string) => {
@@ -982,10 +1010,10 @@ const serverGreeter = server$((firstName: string, lastName: string) => {
9821010

9831011
export default component$(() => (
9841012
<button
985-
onClick$={$(async () => {
1013+
onClick$={async () => {
9861014
const greeting = await serverGreeter('John', 'Doe');
9871015
alert(greeting);
988-
})}
1016+
}}
9891017
>
9901018
greet
9911019
</button>
@@ -1086,5 +1114,61 @@ import Image from '~/media/image.png';
10861114

10871115
</div>
10881116

1117+
1118+
<div class="rule-wrapper">
1119+
<h3 id="serializer-signal-usage">serializer-signal-usage</h3>
1120+
<span>Ensure signals used in update() are also used in deserialize()</span>
1121+
1122+
<h4>serializerSignalMismatch</h4>
1123+
<p>Examples of <b>correct</b> code for this rule:</p>
1124+
<div class="code-wrapper">
1125+
<span class="badge good">✓</span>
1126+
```tsx /countSignal/#a
1127+
import { useSignal, useSerializer$ } from '@qwik.dev/core';
1128+
import MyClass from './my-class';
1129+
1130+
export default component$(() => {
1131+
const countSignal = useSignal(0);
1132+
1133+
useSerializer$(() => ({
1134+
deserialize: () => new MyClass(countSignal.value),
1135+
update: (myClass) => {
1136+
myClass.count = countSignal.value;
1137+
return myClass;
1138+
}
1139+
}));
1140+
1141+
return <div>{myClass.count}</div>;
1142+
});
1143+
```
1144+
</div>
1145+
<p>Examples of <b>incorrect</b> code for this rule:</p>
1146+
<div class="code-wrapper">
1147+
<span class="badge bad">✕</span>
1148+
```tsx /countSignal/#a
1149+
import { useSignal, useSerializer$ } from '@qwik.dev/core';
1150+
import MyClass from './my-class';
1151+
1152+
export default component$(() => {
1153+
const countSignal = useSignal(0);
1154+
1155+
useSerializer$(() => ({
1156+
deserialize: (count) => new MyClass(count),
1157+
initial: 2,
1158+
update: (myClass) => {
1159+
myClass.count = countSignal.value;
1160+
return myClass;
1161+
}
1162+
}));
1163+
1164+
return <div>{myClass.count}</div>;
1165+
});
1166+
```
1167+
<p class="code-description">The countSignal is used in update() but not in deserialize()</p>
1168+
</div>
1169+
<div class="edit-examples-wrapper"><a href="https://github.com/QwikDev/qwik/edit/main/packages/eslint-plugin-qwik/src/serializerSignalUsage.ts" target="_blank" class="edit-btn">Edit examples</a></div>
1170+
1171+
</div>
1172+
10891173
</div>
10901174
</div>

packages/eslint-plugin-qwik/examples.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { jsxNoScriptUrlExamples } from './src/jsxNoScriptUrl';
99
import { loaderLocationExamples } from './src/loaderLocation';
1010
import { noReactPropsExamples } from './src/noReactProps';
1111
import { preferClasslistExamples } from './src/preferClasslist';
12+
import { serializerSignalUsageExamples } from './src/serializerSignalUsage';
1213
import { unusedServerExamples } from './src/unusedServer';
1314
import { useMethodUsageExamples } from './src/useMethodUsage';
1415
import { validLexicalScopeExamples } from './src/validLexicalScope';
@@ -38,4 +39,5 @@ export const examples = {
3839
'jsx-key': jsxKeyExamples,
3940
'unused-server': unusedServerExamples,
4041
'jsx-img': jsxImgExamples,
42+
'serializer-signal-usage': serializerSignalUsageExamples,
4143
};

packages/eslint-plugin-qwik/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { preferClasslist } from './src/preferClasslist';
99
import { unusedServer } from './src/unusedServer';
1010
import { useMethodUsage } from './src/useMethodUsage';
1111
import { validLexicalScope } from './src/validLexicalScope';
12+
import { serializerSignalUsage } from './src/serializerSignalUsage';
1213
import pkg from './package.json';
1314

1415
const rules = {
@@ -23,6 +24,7 @@ const rules = {
2324
'jsx-img': jsxImg,
2425
'jsx-a': jsxAtag,
2526
'no-use-visible-task': noUseVisibleTask,
27+
'serializer-signal-usage': serializerSignalUsage,
2628
};
2729

2830
const recommendedRules = {
@@ -37,6 +39,7 @@ const recommendedRules = {
3739
'qwik/jsx-img': 'warn',
3840
'qwik/jsx-a': 'warn',
3941
'qwik/no-use-visible-task': 'warn',
42+
'qwik/serializer-signal-usage': 'error',
4043
};
4144
const strictRules = {
4245
'qwik/valid-lexical-scope': 'error',
@@ -50,6 +53,7 @@ const strictRules = {
5053
'qwik/jsx-img': 'error',
5154
'qwik/jsx-a': 'error',
5255
'qwik/no-use-visible-task': 'warn',
56+
'qwik/serializer-signal-usage': 'error',
5357
};
5458

5559
const configs = {

0 commit comments

Comments
 (0)