Skip to content

Commit a334492

Browse files
Shinonilesya7lpomerleaunrobertdehault
authored
@W-15205324 New rule - disallow import of scoped modules (#164)
* New rule - disallow import of scoped modules * Addressin the comments * Small correction in the title * Remove the link for formFactor * Remove the rule regarding the dynamic imports * Remove the link to the internal doc * Update docs/rules/no-form-factor-in-ssrable-components.md Co-authored-by: Laura <49494194+lpomerleau@users.noreply.github.com> * Update lib/rules/no-static-imports-of-user-specific-scoped-modules-in-ssrable-components.js Co-authored-by: Laura <49494194+lpomerleau@users.noreply.github.com> * Fixing the failing test * Update lib/rules/no-static-imports-of-user-specific-scoped-modules-in-ssrable-components.js Co-authored-by: nrobertdehault <nrobertdehault@salesforce.com> * Addressing feedabck * Adding the link to the public doc * rebasing * Fix the link. * Renaming * Removing the config --------- Co-authored-by: lturanscaia <lturanscaia@salesforce.com> Co-authored-by: Laura <49494194+lpomerleau@users.noreply.github.com> Co-authored-by: nrobertdehault <nrobertdehault@salesforce.com>
1 parent d05b230 commit a334492

8 files changed

+415
-19
lines changed

README.md

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,46 @@ To choose from three configuration settings, install the [`eslint-config-lwc`](h
6868

6969
### LWC
7070

71-
| Rule ID | Description | Fixable |
72-
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------- |
73-
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
74-
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
75-
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
76-
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
77-
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
78-
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
79-
| [lwc/no-leading-uppercase-api-name](./docs/rules/no-leading-uppercase-api-name.md) | ensure public property doesn't start with an upper-case character | |
80-
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
81-
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
82-
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
83-
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
84-
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
85-
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
86-
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
87-
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
88-
| [lwc/valid-graphql-wire-adapter-callback-parameters](./docs/rules/valid-graphql-wire-adapter-callback-parameters.md) | ensure graphql wire adapters are using 'errors' instead of 'error' | |
89-
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
71+
| Rule ID | Description | Fixable |
72+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------- |
73+
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
74+
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
75+
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
76+
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
77+
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
78+
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
79+
| [lwc/no-leading-uppercase-api-name](./docs/rules/no-leading-uppercase-api-name.md) | ensure public property doesn't start with an upper-case character | |
80+
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
81+
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
82+
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
83+
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
84+
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
85+
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
86+
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
87+
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
88+
| [lwc/valid-graphql-wire-adapter-callback-parameters](./docs/rules/valid-graphql-wire-adapter-callback-parameters.md) | ensure graphql wire adapters are using 'errors' instead of 'error' | |
89+
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
90+
| Rule ID | Description | Fixable |
91+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------- |
92+
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
93+
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
94+
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
95+
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
96+
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
97+
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
98+
| [lwc/no-leading-uppercase-api-name](./docs/rules/no-leading-uppercase-api-name.md) | ensure public property doesn't start with an upper-case character | |
99+
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
100+
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
101+
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
102+
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
103+
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
104+
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
105+
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
106+
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
107+
| [lwc/valid-graphql-wire-adapter-callback-parameters](./docs/rules/valid-graphql-wire-adapter-callback-parameters.md) | ensure graphql wire adapters are using 'errors' instead of 'error' | |
108+
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
109+
| [lwc/ssr-no-static-imports-of-user-specific-scoped-modules](./docs/rules/ssr-no-static-imports-of-user-specific-scoped-modules.md) | disallow static imports of user-specific scoped modules in SSR-able components | |
110+
| [lwc/ssr-no-form-factor](./docs/rules/ssr-no-form-factor.md) | disallow formFactor in SSR-able components | |
90111

91112
### Best practices
92113

docs/rules/ssr-no-form-factor.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Using @salesforce/client/formFactor in SSR-able components is not the best practice(`lwc/ssr-no-form-factor`)
2+
3+
## Rule details
4+
5+
The [`@salesforce/client/formFactor`](https://developer.salesforce.com/docs/platform/lwc/guide/create-client-form-factor.html) module defaults to a value of `"Large"` during SSR, regardless of the device that made the request. This can cause issues where the UI shifts once client-side rendering is complete, particularly when rendering on smaller devices. To avoid this, use **[CSS media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries)** to handle form factors and **[responsive images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)** for different screen sizes.
6+
7+
Example of **incorrect** code:
8+
9+
```js
10+
import FORM_FACTOR from '@salesforce/client/formFactor';
11+
export default class Sample extends LightningElement {
12+
classes = '';
13+
connectedCallback() {
14+
this.classes = FORM_FACTOR === 'small' ? 'mobile' : '';
15+
}
16+
}
17+
```
18+
19+
```html
20+
<template>
21+
<div class="{classes}">
22+
<div class="col-3"><!-- ... --></div>
23+
<div class="col-6"><!-- ... --></div>
24+
<div class="col-3"><!-- ... --></div>
25+
</div>
26+
</template>
27+
```
28+
29+
```css
30+
.col-3 {
31+
width: 25%;
32+
}
33+
.col-6 {
34+
width: 50%;
35+
}
36+
.mobile .col-3 {
37+
width: 100%;
38+
}
39+
.mobile .col-6 {
40+
width: 100%;
41+
}
42+
```
43+
44+
Example of **correct** code:
45+
46+
```js
47+
export default class Sample extends LightningElement {}
48+
```
49+
50+
```html
51+
<template>
52+
<div>
53+
<div class="col-3"><!-- ... --></div>
54+
<div class="col-6"><!-- ... --></div>
55+
<div class="col-3"><!-- ... --></div>
56+
</div>
57+
</template>
58+
```
59+
60+
```css
61+
.col-3 {
62+
width: 25%;
63+
}
64+
.col-6 {
65+
width: 50%;
66+
}
67+
68+
/* mobile */
69+
@media (max-width: 768px) {
70+
[class*='col-'] {
71+
width: 100%;
72+
}
73+
}
74+
```
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Deprecation Notice for Salesforce Scoped Modules during Server-Side Rendering (SSR)
2+
3+
Static imports of user-specific scoped modules, such as `@salesforce/user/*`, are not supported in LWC components marked with `lightning__ServerRenderable` or `lightning__ServerRenderableWithHydration`.
4+
5+
## Rule details
6+
7+
The following Salesforce scoped modules are deprecated when using Server-Side Rendering (SSR):
8+
9+
- `@salesforce/user/*`
10+
- `@salesforce/userPermission/*`
11+
- `@salesforce/customPermission/*`
12+
13+
To replace these deprecated modules, use dynamic imports to fetch the necessary data on-demand. This ensures that SSR can run without issues and user-specific data is loaded appropriately after hydration on the client-side. For more details, refer to the guide on [Adding Dynamic Data to LWR Sites](https://developer.salesforce.com/docs/atlas.en-us.exp_cloud_lwr.meta/exp_cloud_lwr/advanced_expressions.htm).
14+
15+
### Handling Deprecation in components
16+
17+
If your component relies on one of these scoped modules, follow these best practices:
18+
19+
1. **On the Server (SSR)**:
20+
21+
- Render a placeholder during SSR to avoid negatively impacting **[web vitals](https://web.dev/articles/vitals)**.
22+
23+
2. **On the Client (after Hydration)**:
24+
- **[Dynamically import](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-portable-best-practices.html#dynamically-import-non-portable-modules)** the module after hydration to avoid SSR issues.
25+
26+
```html
27+
<template>
28+
<template lwc:if="{userId}"><c-user-profile user-id="{userId}"></c-user-profile></template>
29+
<!-- Rendering a placeholder avoids layout shifts on the client when the user ID is loaded -->
30+
<template lwc:else><c-user-placeholder></c-user-placeholder></template>
31+
</template>
32+
```
33+
34+
Example of **incorrect** code:
35+
36+
```js
37+
import { LightningElement } from 'lwc';
38+
import Id from '@salesforce/user/Id';
39+
40+
export default class UserProfile extends LightningElement {
41+
userId = Id;
42+
}
43+
```
44+
45+
Example of **correct** code:
46+
47+
```js
48+
import { LightningElement } from 'lwc';
49+
50+
export default class UserProfile extends LightningElement {
51+
userId;
52+
53+
async connectedCallback() {
54+
if (!import.meta.env.SSR) {
55+
// Only load user-specific scoped modules on the client
56+
// This logic requires hydration of the component
57+
this.userId = await import('@salesforce/user/Id');
58+
}
59+
}
60+
}
61+
```

lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const rules = {
3434
'no-node-env-in-ssr': require('./rules/no-node-env-in-ssr'),
3535
'ssr-no-unsupported-node-api': require('./rules/ssr-no-unsupported-node-api'),
3636
'no-host-mutation-in-connected-callback': require('./rules/no-host-mutation-in-connected-callback'),
37+
'ssr-no-static-imports-of-user-specific-scoped-modules': require('./rules/ssr-no-static-imports-of-user-specific-scoped-modules'),
38+
'ssr-no-form-factor': require('./rules/ssr-no-form-factor'),
3739
};
3840

3941
module.exports = {

0 commit comments

Comments
 (0)