JavaScript¶
TODO(David)
Write this page. Stub created for structure only.
TODO
LWC JavaScript conventions
Standards for the JavaScript of Lightning Web Components and Aura components: the choice between the two, security, the API version, readability, and linting.
The standards of a Lightning component as a component, the choice of a Lightning Web Component over Aura and the standards that keep a component working under Lightning Web Security, are on Lightning Web Components and Aura. This page holds the standards of the language, wherever it runs.
Some standards on this page have an ESLint rule and a PMD rule; each says whether an organisation can switch off the PMD or the ESLint rule if duplicate violation reports are a problem.
Security¶
Standards that keep what a component knows from reaching anyone who should not have it, under the principle of least privilege (PLP).
Console and Debugger Statements¶
Console output is a security control, not a style preference, and it is more exposed than Apex debug logs: anyone who opens the browser's developer tools can read it, with no special permission in the org. The platform does not strip console statements from production, so lint-time enforcement is the only guard.
- No
consolestatements (console.log,console.warn,console.error, and the rest), in a component or in a Jest test. A console statement is treated like a debug statement in Apex, and blocks the pull request (severity 2); enforced by the ESLint ruleno-consoleand the PMD JavaScript ruleAvoidConsoleStatements.no-consolealso checks the first statement of a class method, so an organisation can switch offAvoidConsoleStatementsif the duplicate violations are a problem. - No
debugger;statements; enforced by the ESLint ruleno-debugger. A leftoverdebugger;pauses execution for anyone with developer tools open. - Client-side diagnostics go through the org's logging framework, which logs from Lightning Web Components and Aura as well as Apex and Flow; see Observability.
- Debug Mode stays off for production users: it serves unminified framework and component code, which slows every page and makes the client side easier to read and inspect. No rule checks this, because Debug Mode is a setting on each user in the org and not in the repository; an administrator checks the Debug Mode Users list in Setup.
Cookies¶
- A cookie that JavaScript sets has the
Secureattribute, as indocument.cookie = 'theme=dark; Secure'. Without it, the browser can send the cookie over an unencrypted connection. A cookie with noSecureattribute blocks the pull request (severity 2); enforced byno-restricted-syntax, with a selector of the sample ESLint file, andRequireSecureCookie. The selector also finds a cookie set in the first statement of a method, so an organisation can switch offRequireSecureCookieif the duplicate violations are a problem.
Third-Party Libraries¶
A library that the org serves to its users is part of the org's attack surface, whether it is bundled in a component or loaded from a static resource (Supply Chain Security). A library bundled into a Lightning web component or a static resource also carries the vulnerability history of the version that was copied in, and it is easily forgotten: Renovate and Dependabot update the dependencies that a manifest such as package.json declares, and a copied library is declared nowhere (Version Control: Renovate and Dependabot). A scan on every pull request keeps it in view.
- No JavaScript library with a known vulnerability is used. RetireJS, through Code Analyzer, matches every library it finds in the repository against its register of published vulnerabilities (Retire.js). A library with a known vulnerability of critical severity blocks the pull request (severity 1), and so does one of high severity (severity 2); medium is reported at severity 3, and low at severity 4. The fix is the same in every case: the library is raised to a version with no known vulnerability, or removed. Enforced by
LibraryWithKnownCriticalSeverityVulnerability,LibraryWithKnownHighSeverityVulnerability,LibraryWithKnownMediumSeverityVulnerability, andLibraryWithKnownLowSeverityVulnerability.
Strings Run as Code¶
Code written inside a string is not checked by the linter or the formatter, and it is where input from outside becomes running code.
setTimeoutandsetIntervaltake a function, never a string of code. A string blocks the pull request (severity 2); enforced by the ESLint ruleno-implied-eval.- No
javascript:URL is written in JavaScript. One blocks the pull request (severity 2); enforced by the ESLint ruleno-script-url.
Salesforce's own ESLint configurations, for Aura components and for Lightning Web Components, make both rules an error, and these standards do not rank a rule that they name below the level that Salesforce gives it (Version Control: ESLint).
Sources:
API Version¶
A Lightning Web Component has an API version of its own, in its .js-meta.xml file, and the framework treats the component as it did in the release of that version (Component Versioning). Two examples, from the framework's own list:
- From API 59.0, every HTML parsing error in a template stops the component compiling. Before that, some were only warnings.
- From API 62.0, a template can bind an object or an array to
class. An earlier version does not support the binding.
A component at an old API version blocks the pull request (severity 2). Enforced by BumpApiVersion and AvoidOldSalesforceApiVersions. The standard is in Metadata Standards: API Version.
Readability¶
Code Style¶
Code style standards change nothing about what the code does, and they make it easier to read. Prettier settles many of them in a file that it formats. A rule is the check for the rest, and for a file that Prettier did not format.
- No redundant parentheses: an expression is not wrapped in a second pair, as in
((total)). Prettier removes them. One that remains is reported and does not block the pull request (severity 4); enforced by the PMD JavaScript ruleUnnecessaryParentheses. - No block that does nothing: a pair of braces that belongs to no statement and declares nothing of its own is removed. A
varinside such braces still belongs to the whole function, so the braces suggest a scope that they do not make. Braces that hold alet,const, orclassdeclaration do make a scope, and stay: acaseof aswitchmay have braces to hold aconstof its own. Prettier does not remove a block. A block that does nothing is reported and does not block the pull request (severity 3); enforced by the ESLint ruleno-lone-blocks. - No
elseafter anifblock that ends inreturn: the code after theifis theelse. The extra level of nesting hides the main path of the function. Anelseafter areturnblocks the pull request (severity 2); enforced by the ESLint ruleno-else-returnand the PMD JavaScript ruleNoElseReturn. Keep both rules:NoElseReturnalso reports anelse ifafter areturn, whichno-else-returnallows.
Formatting¶
The opening curly bracket sits on the same line as the declaration or statement that it opens, which is how Prettier formats JavaScript. In JavaScript this is more than style: a return followed by a line break ends the statement, so an object whose bracket starts on the next line is never returned.
Comments¶
The General: Comments standards apply to JavaScript. These are particular to it.
- The Salesforce Documenter (SFDoc) extension writes its default author,
ChangeMeIn@UserSettingsUnder.SFDoc, into the JavaScript file of a Lightning Web Component or an Aura component when its setting for Lightning JavaScript is on. That string in a JavaScript file blocks the pull request (severity 2). Enforced byNoSfdocDefaultAuthor. - An
eslint-disablecomment, or an inline configuration comment that turns a rule off, blocks the pull request (severity 2) until the violation is fixed or the owning team approves the suppression. Enforced byNoUnapprovedEslintSuppressionand@lwc/lwc-platform/no-inline-disable. TODO,TO-DO,FIXME,FIX-ME, orHACKas the first word of a comment line, or with a colon after it, is reported in every JavaScript file, static resources included, so a third-party library that still holds such a comment is reported too, which shows what the library leaves unfinished. One that remains is reported at severity 5. Enforced byNoUnresolvedTodosJavaScript.
Complexity¶
A function is simple enough to read in one pass. These rules count what makes a function hard to follow, and a count is a signal for a reviewer, not proof of a problem (Maintainability), so none of them blocks a pull request. Each limit is set in the sample ESLint configuration, where an organisation may change it.
- Blocks nest no more than 3 deep in a function. An
if,for,while,do,switch, ortryinside three others is reported at severity 3, the same depth and severity as nestedifstatements in Apex (Complexity and Unused Code). Enforced bymax-depth. - A function has a cyclomatic complexity of 10 or less, the limit that Apex uses. A function with more paths through it is reported at severity 4. Enforced by
complexity. - A function has no more than 50 lines of code. Blank lines and comments do not count, so the rule never discourages a comment. A longer function is reported at severity 4. Enforced by
max-lines-per-function.
Naming¶
The naming standards for JavaScript are on the General: Naming page, with the naming standards of the other languages.
Required Formatting¶
Duplicated Code¶
- No block of code is copied from one component to another. A duplicated block is a function in a shared module that both import, so that a fix is made once. A block of JavaScript or TypeScript that appears more than once across the repository is reported at severity 5, for the developer's information (Refactoring Duplicates). Enforced by
DetectCopyPasteForJavascriptandDetectCopyPasteForTypescript.
Error Prevention¶
Braces¶
The body of every if, else, for, and while is wrapped in braces, even when it is one line. A missing pair of braces blocks the pull request (severity 2); enforced by the ESLint rule curly and the PMD JavaScript rules IfStmtsMustUseBraces, IfElseStmtsMustUseBraces, ForLoopsMustUseBraces, and WhileLoopsMustUseBraces. curly also checks do loops, so an organisation can switch off the {N} PMD brace rules if the duplicate violations are a problem.
The reason is the one given for Apex (Braces): JavaScript reads braces, not indentation, so a line added later under an unbraced if runs every time, however it is indented. That is the mistake behind Apple's "goto fail" bug (CVE-2014-1266). Google's JavaScript style requires braces on every control structure for the same reason, and ESLint's curly rule exists to enforce it. The standard is about control statements: an arrow function with an expression body, such as item => item.id, has no block to brace.
Assignments in Conditions¶
No condition holds an assignment. The condition of an if, while, do, or for statement has no =, ++, or -- in it, and neither has any part of a ternary expression (a ? b : c). An = where a comparison was meant, as in if (total = 0), is a common bug: the value is assigned, and the condition then tests the value assigned. Where an assignment is meant, it is a statement of its own. The update part of a for loop, as in i++, is not a condition, and is allowed. To read every match of a global regular expression, use for (const match of text.matchAll(pattern)), not a while loop that assigns in its condition. An assignment in a condition blocks the pull request (severity 2); enforced by the ESLint rule no-cond-assign and the PMD JavaScript rule AssignmentInOperand.
Unreachable Code¶
No statement follows a return, throw, break, or continue in the same block. Such a statement never runs. It is dead code, which is removed, or a sign that the jump is in the wrong place. A statement that can never run blocks the pull request (severity 2); enforced by the ESLint rule no-unreachable and the PMD JavaScript rule UnreachableCode.
With Statements¶
No with statement. Inside one, a name can be a property of the object or a variable outside it, and the reader cannot tell which. A with statement blocks the pull request (severity 2); enforced by the PMD JavaScript rule AvoidWithStatement (severity 2) and the ESLint rule no-with (severity 3). Keep both rules. In a Lightning web component, a with statement stops ESLint with a parse error, and AvoidWithStatement misses a with statement that is the first statement of a method.
Equality¶
Values are compared with === and !==, not == and !=. The loose operators convert the types first, so '0' == false is true. The one exception is value == null, which tests for null and undefined together. A loose comparison blocks the pull request (severity 2); enforced by the ESLint rule eqeqeq and the PMD JavaScript rule EqualComparison. eqeqeq reports every comparison that EqualComparison reports, and more, so an organisation can switch off EqualComparison if the duplicate violations are a problem.
Return Values¶
A function returns a value on every path, or on none. A function that returns a value on one path and nothing on another gives its caller undefined with no warning. A mixed return blocks the pull request (severity 2); enforced by the ESLint rule consistent-return and the PMD JavaScript rule ConsistentReturn. consistent-return also checks the methods of a class, so an organisation can switch off ConsistentReturn if the duplicate violations are a problem.
Number Parsing¶
parseInt is always given a radix, as in parseInt(value, 10). Without one, a string that starts with 0x is read as hexadecimal. A call with no radix blocks the pull request (severity 2); enforced by the PMD JavaScript rule UseBaseWithParseInt and the ESLint rule radix. radix also checks Number.parseInt, so an organisation can switch off UseBaseWithParseInt if the duplicate violations are a problem.
Symbol Descriptions¶
A Symbol is created with a description, as in Symbol('rowId'), and not as Symbol(). A symbol that is logged shows its description, which makes it easier to debug (symbol-description); a symbol with none cannot be told from another in a log or an error message. A Symbol() with no description is reported at severity 3 and does not block the pull request; enforced by the ESLint rule symbol-description.
Sources:
Formatting/Linting¶
The JavaScript of Aura components and Lightning Web Components is checked by ESLint, from the one ESLint configuration file of the repository. The file, the packages that it loads, and how it runs are in Version Control: ESLint.
SonarJS¶
These standards recommend adding eslint-plugin-sonarjs, SonarSource's JavaScript rules for ESLint, to each organisation's project. Its rules handle five standards for component JavaScript:
- No hard-coded passwords or other credentials (
sonarjs/no-hardcoded-passwords). - No weak hashing algorithms, such as MD5 and SHA-1 (
sonarjs/hashing). - No clear-text protocols, such as
httpandftp, in URLs (sonarjs/no-clear-text-protocols). - No hard-coded IP addresses (
sonarjs/no-hardcoded-ip). - No conditional whose branches are all the same (
sonarjs/no-all-duplicated-branches).
Sources:
Testing¶
TODO
Consider moving this section to the Testing page (testing.md, Content item 144).
TODO
Jest unit tests for LWC, mocking patterns, coverage expectations
- The Jest tests run on a pull request only when its delta package holds a Lightning web component, and a failing test blocks the pull request. Enforced by the LWC Jest Tests workflow.
- A Lightning web component that has a test has at least 80% line coverage, counted over all its JavaScript files, and a tested component below 80% blocks the pull request. The threshold is in
jest.config.jsfrom the first day of a project, because a coverage requirement added later to a component library with little coverage is much harder to meet (Version Control: Jest). Enforced byjest.config.jsand the LWC Jest Tests workflow. - A component with no test file is named in the report of the run, and it does not fail the gate. Enforced by the LWC Jest Tests workflow.
- The failing tests, each with its message, are in a table on the pull request and in the job summary, not in a log. Enforced by the LWC Jest Tests workflow, which reads the JSON output of Jest.
Rules¶
The rules that this page mentions. The Standard column gives each rule's standard in one line, and the Section column links to the section that mentions the rule; "Not yet written" marks a rule of this page that no section mentions yet. Appendix F.6 holds the full cross-reference.
| Rule | Kind | Severity | Standard | Section |
|---|---|---|---|---|
Library |
Stock Code Analyzer RetireJS | 1 | No JavaScript library with a known vulnerability of critical severity | Third-Party Libraries |
@lwc/ |
Stock Code Analyzer ESLint | 2 | No comment that switches off an ESLint rule for a line | Comments |
Assignment |
Stock PMD | 2 | No assignment in a condition: no =, ++, or -- in an if, while, do, for, or ternary test |
Assignments in Conditions |
Avoid |
Stock PMD | 2 | No console statements, in a component or in a Jest test |
Console and Debugger Statements |
Avoid |
Stock Code Analyzer Regex | 2 | Every metadata file states a recent API version | API Version |
Avoid |
Stock PMD | 2 | No with statement |
With Statements |
Bump |
Custom PMD XPath | 2 | Every metadata file is at or above the API version floor Notes on this rule. | API Version |
consistent-return |
Stock Code Analyzer ESLint | 2 | A function returns a value on every path, or on none | Return Values |
Consistent |
Stock PMD | 2 | A function returns a value on every path, or on none | Return Values |
curly |
Stock Code Analyzer ESLint | 2 | Wrap the body of every if, else, for, and while in braces, even one line |
Braces |
eqeqeq |
Stock Code Analyzer ESLint | 2 | Compare values with === and !==, not == and !=; value == null is the one exception |
Equality |
Equal |
Stock PMD | 2 | Compare values with === and !==, not == and !=; value == null is the one exception |
Equality |
For |
Stock PMD | 2 | Wrap the body of every for loop in braces, even one line |
Braces |
If |
Stock PMD | 2 | Wrap the body of every else in braces, even one line |
Braces |
If |
Stock PMD | 2 | Wrap the body of every if in braces, even one line |
Braces |
Library |
Stock Code Analyzer RetireJS | 2 | No JavaScript library with a known vulnerability of high severity | Third-Party Libraries |
no-cond-assign |
Stock Code Analyzer ESLint | 2 | No assignment in a condition | Assignments in Conditions |
no-console |
Stock Code Analyzer ESLint | 2 | No console statements |
Console and Debugger Statements |
no-debugger |
Stock Code Analyzer ESLint | 2 | No debugger statements |
Console and Debugger Statements |
no-else-return |
Stock Code Analyzer ESLint | 2 | No else after an if block that ends in return |
Code Style |
no-implied-eval |
Stock Code Analyzer ESLint | 2 | setTimeout and setInterval take a function, never a string of code |
Strings Run as Code |
no-restricted-syntax |
Stock Code Analyzer ESLint | 2 | No variable, parameter, function, class, or import named top or location |
Cookies |
no-script-url |
Stock Code Analyzer ESLint | 2 | No javascript: URL is written in JavaScript |
Strings Run as Code |
no-unreachable |
Stock Code Analyzer ESLint | 2 | No statement follows a return, throw, break, or continue in the same block |
Unreachable Code |
No |
Stock PMD | 2 | No else after an if block that ends in return |
Code Style |
No |
Custom Code Analyzer Regex | 2 | The author is a person or a team, never a tool default such as ChangeMeIn@UserSettingsUnder.SFDoc |
Comments |
No |
Custom Code Analyzer Regex | 2 | No ESLint suppression without an approving review from the owning team | Comments |
radix |
Stock Code Analyzer ESLint | 2 | parseInt is always given a radix, as in parseInt(value, 10) |
Number Parsing |
Require |
Custom PMD XPath | 2 | A cookie that JavaScript sets has the Secure attribute |
Cookies |
Unreachable |
Stock PMD | 2 | No statement follows a return, throw, break, or continue in the same block |
Unreachable Code |
Use |
Stock PMD | 2 | parseInt is always given a radix, as in parseInt(value, 10) |
Number Parsing |
While |
Stock PMD | 2 | Wrap the body of every while loop in braces, even one line |
Braces |
Library |
Stock Code Analyzer RetireJS | 3 | No JavaScript library with a known vulnerability of medium severity | Third-Party Libraries |
max-depth |
Stock Code Analyzer ESLint | 3 | Blocks nest no more than 3 deep in a function | Complexity |
no-lone-blocks |
Stock Code Analyzer ESLint | 3 | No block that belongs to no statement and declares nothing | Code Style |
no-with |
Stock Code Analyzer ESLint | 3 | No with statement |
With Statements |
symbol-description |
Stock Code Analyzer ESLint | 3 | A Symbol is created with a description, as in Symbol('rowId') |
Symbol Descriptions |
complexity |
Stock Code Analyzer ESLint | 4 | A function has a cyclomatic complexity of 10 or less | Complexity |
Library |
Stock Code Analyzer RetireJS | 4 | No JavaScript library with a known vulnerability of low severity | Third-Party Libraries |
max-lines-per-function |
Stock Code Analyzer ESLint | 4 | A function has no more than 50 lines of code, not counting blank lines and comments | Complexity |
Unnecessary |
Stock PMD | 4 | No redundant parentheses around an expression | Code Style |
Detect |
Stock Code Analyzer CPD | 5 | No copied blocks of JavaScript; share a function in a module | Duplicated Code |
Detect |
Stock Code Analyzer CPD | 5 | No copied blocks of TypeScript; share a function in a module | Duplicated Code |
No |
Custom Code Analyzer Regex | 5 | No TODO, TO-DO, FIXME, FIX-ME, or HACK comment stays in JavaScript |
Comments |