Skip to content

Commit

Permalink
@W-15205324 New rule - disallow import of scoped modules (#164)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* Update lib/rules/no-static-imports-of-user-specific-scoped-modules-in-ssrable-components.js

Co-authored-by: Laura <[email protected]>

* Fixing the failing test

* Update lib/rules/no-static-imports-of-user-specific-scoped-modules-in-ssrable-components.js

Co-authored-by: nrobertdehault <[email protected]>

* Addressing feedabck

* Adding the link to the public doc

* rebasing

* Fix the link.

* Renaming

* Removing the config

---------

Co-authored-by: lturanscaia <[email protected]>
Co-authored-by: Laura <[email protected]>
Co-authored-by: nrobertdehault <[email protected]>
  • Loading branch information
4 people authored Oct 18, 2024
1 parent d05b230 commit a334492
Show file tree
Hide file tree
Showing 8 changed files with 415 additions and 19 deletions.
59 changes: 40 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,25 +68,46 @@ To choose from three configuration settings, install the [`eslint-config-lwc`](h

### LWC

| Rule ID | Description | Fixable |
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------- |
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
| [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 | |
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
| [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' | |
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
| Rule ID | Description | Fixable |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------- |
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
| [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 | |
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
| [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' | |
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
| Rule ID | Description | Fixable |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------- |
| [lwc/consistent-component-name](./docs/rules/consistent-component-name.md) | ensure component class name matches file name | 🔧 |
| [lwc/no-api-reassignments](./docs/rules/no-api-reassignments.md) | prevent public property reassignments | |
| [lwc/no-deprecated](./docs/rules/no-deprecated.md) | disallow usage of deprecated LWC APIs | |
| [lwc/no-document-query](./docs/rules/no-document-query.md) | disallow DOM query at the document level | |
| [lwc/no-attributes-during-construction](./docs/rules/no-attributes-during-construction.md) | disallow setting attributes during construction | |
| [lwc/no-disallowed-lwc-imports](./docs/rules/no-disallowed-lwc-imports.md) | disallow importing unsupported APIs from the `lwc` package | |
| [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 | |
| [lwc/no-unexpected-wire-adapter-usages](./docs/rules/no-unexpected-wire-adapter-usages.md) | enforce wire adapters to be used with `wire` decorator | |
| [lwc/no-unknown-wire-adapters](./docs/rules/no-unknown-wire-adapters.md) | disallow usage of unknown wire adapters | |
| [lwc/valid-api](./docs/rules/valid-api.md) | validate `api` decorator usage | |
| [lwc/valid-track](./docs/rules/valid-track.md) | validate `track` decorator usage | |
| [lwc/valid-wire](./docs/rules/valid-wire.md) | validate `wire` decorator usage | |
| [lwc/no-restricted-browser-globals-during-ssr](./docs/rules/no-restricted-browser-globals-during-ssr.md) | disallow access to global browser APIs during SSR | |
| [lwc/no-unsupported-ssr-properties](./docs/rules/no-unsupported-ssr-properties.md) | disallow access of unsupported properties in SSR | |
| [lwc/no-node-env-in-ssr](./docs/rules/no-node-env-in-ssr.md) | disallow usage of process.env.NODE_ENV in SSR | |
| [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' | |
| [lwc/no-host-mutation-in-connected-callback](./docs/rules/no-host-mutation-in-connected-callback.md) | disallow the host element mutation in 'connectedCallback' | |
| [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 | |
| [lwc/ssr-no-form-factor](./docs/rules/ssr-no-form-factor.md) | disallow formFactor in SSR-able components | |

### Best practices

Expand Down
74 changes: 74 additions & 0 deletions docs/rules/ssr-no-form-factor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Using @salesforce/client/formFactor in SSR-able components is not the best practice(`lwc/ssr-no-form-factor`)

## Rule details

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.

Example of **incorrect** code:

```js
import FORM_FACTOR from '@salesforce/client/formFactor';
export default class Sample extends LightningElement {
classes = '';
connectedCallback() {
this.classes = FORM_FACTOR === 'small' ? 'mobile' : '';
}
}
```

```html
<template>
<div class="{classes}">
<div class="col-3"><!-- ... --></div>
<div class="col-6"><!-- ... --></div>
<div class="col-3"><!-- ... --></div>
</div>
</template>
```

```css
.col-3 {
width: 25%;
}
.col-6 {
width: 50%;
}
.mobile .col-3 {
width: 100%;
}
.mobile .col-6 {
width: 100%;
}
```

Example of **correct** code:

```js
export default class Sample extends LightningElement {}
```

```html
<template>
<div>
<div class="col-3"><!-- ... --></div>
<div class="col-6"><!-- ... --></div>
<div class="col-3"><!-- ... --></div>
</div>
</template>
```

```css
.col-3 {
width: 25%;
}
.col-6 {
width: 50%;
}

/* mobile */
@media (max-width: 768px) {
[class*='col-'] {
width: 100%;
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Deprecation Notice for Salesforce Scoped Modules during Server-Side Rendering (SSR)

Static imports of user-specific scoped modules, such as `@salesforce/user/*`, are not supported in LWC components marked with `lightning__ServerRenderable` or `lightning__ServerRenderableWithHydration`.

## Rule details

The following Salesforce scoped modules are deprecated when using Server-Side Rendering (SSR):

- `@salesforce/user/*`
- `@salesforce/userPermission/*`
- `@salesforce/customPermission/*`

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).

### Handling Deprecation in components

If your component relies on one of these scoped modules, follow these best practices:

1. **On the Server (SSR)**:

- Render a placeholder during SSR to avoid negatively impacting **[web vitals](https://web.dev/articles/vitals)**.

2. **On the Client (after Hydration)**:
- **[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.

```html
<template>
<template lwc:if="{userId}"><c-user-profile user-id="{userId}"></c-user-profile></template>
<!-- Rendering a placeholder avoids layout shifts on the client when the user ID is loaded -->
<template lwc:else><c-user-placeholder></c-user-placeholder></template>
</template>
```

Example of **incorrect** code:

```js
import { LightningElement } from 'lwc';
import Id from '@salesforce/user/Id';

export default class UserProfile extends LightningElement {
userId = Id;
}
```

Example of **correct** code:

```js
import { LightningElement } from 'lwc';

export default class UserProfile extends LightningElement {
userId;

async connectedCallback() {
if (!import.meta.env.SSR) {
// Only load user-specific scoped modules on the client
// This logic requires hydration of the component
this.userId = await import('@salesforce/user/Id');
}
}
}
```
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const rules = {
'no-node-env-in-ssr': require('./rules/no-node-env-in-ssr'),
'ssr-no-unsupported-node-api': require('./rules/ssr-no-unsupported-node-api'),
'no-host-mutation-in-connected-callback': require('./rules/no-host-mutation-in-connected-callback'),
'ssr-no-static-imports-of-user-specific-scoped-modules': require('./rules/ssr-no-static-imports-of-user-specific-scoped-modules'),
'ssr-no-form-factor': require('./rules/ssr-no-form-factor'),
};

module.exports = {
Expand Down
Loading

0 comments on commit a334492

Please sign in to comment.