Lint Validations

When running the fdk validate and fdk pack commands on an app, the Freshworks Developer Kit (FDK) performs linting on the app’s source code and reports possible errors. The rules that the linter goes through are a collection of best practices that the app should adhere to. Violations are displayed based on the severity category (Warning/Error) and they must be fixed before the app is submitted for approval to the Freshworks Marketplace.

Adhering to these rules ensures that the app conforms to the quality and security standards of a Freshworks Marketplace app.

  • Avoid cross scope variable assignment (no-cross-scope-assign)

    Category: Warning

    Description: When writing asynchronous code, avoid declaring and assigning variables in different scopes as this can create subtle race condition bugs.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //Variable Declaration var dummyVar; $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { //Variable definition dummyVar = 9; }); }) .catch(function (err) { if (err) { console.error('Error - ', err); } }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { var dummyVar = 9; // Rest of the logic that makes use of 'dummyVar' ... }); }) .catch(function (err) { if (err) { console.error('Error - ', err); } }); });
    EXPAND ↓
  • No unhandled promises (no-unhandled-promise)

    Category: Warning

    Description: Forgetting to handle promise rejections can lead to ambiguous behavior in the application.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { //Some logic }); }) });

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { //Some logic }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓
  • Disallow Non-RequestAPI calls in the front end (no-non-client-request-model)

    Category: Warning

    Description: Ensure that REST API calls are made from front-end apps using the Request API to subvert many security issues and to work within the CORS policy of a browser.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { $.get("https://www.example.com", function (data) { // Some logic based on the returned data }); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { client.request.get("https://www.example.com", {}) .then( function (data) { //handle "data" //"data" is a json string with status, headers, and response. }, function (error) { //handle failure } ); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓
  • Disallow logging just the error object during promise rejections (no-logging-rejections)

    Category: Warning

    Description: In promise rejection handlers, logging only the error object is not sufficient. The error should be reported (via notifications) to users/agents, in layperson's terms, so that they can undertake subsequent actions.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { // Some logic }); }) .catch(function (err) { console.error('Error - ', err); }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { // Some logic }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓
  • Remove unused dependencies and avoid the use of unlisted dependencies (no-dependency-mismatch)

    Category: Error

    Description: In serverless applications, avoid using dependencies that are declared but left unused and dependencies that are used but undeclared.

    Example of Incorrect Code

    manifest.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    { "platform-version": "2.0", "product": { "freshdesk": { "location": { "ticket_sidebar": { "url": "template.html", "icon": "icon.svg" } } } } }
    EXPAND ↓

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 'lodash' is required without being listed as a dependency! var _ = require('lodash'); exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (args) { console.log('Hello ' + args['data']['requester']['name']); } };

    Example of Correct Code

    manifest.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    { "platform-version": "2.0", "dependencies": { "lodash": "4.0.0" }, "product": { "freshdesk": { "location": { "ticket_sidebar": { "url": "template.html", "icon": "icon.svg" } } } } }
    EXPAND ↓

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var _ = require('lodash'); exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (args) { console.log('Hello ' + args['data']['requester']['name']); // Some logic with lodash } };
  • Limit cyclomatic complexity (complexity)

    Category: Warning

    Description: Cyclomatic complexity measures the number of linearly independent paths through a program's source code. The current threshold is 7.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { var a, b, c, d, e, f, g = 1; if (a == 0) { // some logic } else if (b == 2) { // some logic } else if (c === 4) { // some logic } else if (d == e) { // some logic } else if (g > d) { // some logic } else if (d > e) { // some logic } else if (c > a + f) { // some logic } else { // some logic } }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // Extracting sub-functionality into seperate functions is a good way to reduce code complexity $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { var a, b, c, d, e, f, g = 1; //Make the code modular var resOne = doSomething1(a, b, c); var resTwo = doSomething2(d, e, g); if (resOne === resTwo) { doSomething3(a,c,f); } }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error.. } }); }); function doSomething1(a, b, c) { // return some value after executing this fn. if (a == 0) { // some logic } else if (b == 2) { // some logic } else if (c === 4) { // some logic } } function doSomething2(d, e, g) { // return some value after executing this fn. if (d == e) { // some logic } else if (g > d) { // some logic } else if (d > e) { // some logic } } function doSomething3(a, c, f) { // return some value after executing this fn. if (c > a + f) { // some logic } else { // some logic } }
    EXPAND ↓
  • Limit the depth of nested callbacks (max-nested-callbacks)

    Category: Warning

    Description: In asynchronous programming, when using a callback function, a common pitfall is nesting callbacks. The deeper callbacks are nested, the more difficult it is to read the code. The threshold of nested callbacks is 4.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { //Getting Logged-In user client.data.get("loggedInUser").then( function (data) { //Get the contact client.data.get("contact").then( function (data) { //Get the company client.data.get("company").then( function (data) { //Success output //data: {....} }, function (error) { // failure operation } ); }, function (error) { // failure operation } ); }, function (error) { // failure operation } ); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // Promise chaining is one way to prevent callback hell $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { var dataObj = {}; client.data.get("loggedInUser") .then(function (loggedInUser) { //1. Getting Logged-In user dataObj.loggedInUser = loggedInUser; return client.data.get("contact"); }) .then(function (contact) { //2. Getting the contact dataObj.contact = contact; return client.data.get("company"); }) .then(function (company) { dataObj.company = company; //3. Getting the company //Do something with the 'dataObj' }) .catch(function (err) { if (err) { console.error('Error - ', err); // Handle error if something goes wrong } }); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓
  • Enforce Callback Error Handling (handle-callback-err)

    Category: Warning

    Description: The callback pattern, typically used in asynchronous programming, expects an error object or null as the first argument of the callback. Forgetting to handle this can lead to ambiguous behavior in the application.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { // Some logic }); }) .catch(function (err) { }); });
    EXPAND ↓

    Example of Correct Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { // Some logic }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓
  • Disallow use of caller/callee (no-caller)

    Category: Warning

    Description: The use of arguments.caller and arguments.callee is deprecated in JavaScript. Avoid using these in ECMAScript 5, in strict mode.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { var callee = arguments.callee; // Some logic dealing with callee }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Correct Approach

    Avoid using arguments.caller and arguments.callee.

  • Disallow process.env (no-process-env)

    Category: Warning

    Description: The process.env object in Node.js is used to store deployment or configuration parameters. Littering it throughout a project can lead to maintenance issues, as it is another kind of global dependency.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (args) { var config = process.env; // Some logic } };

    Correct Approach

    Avoid using process.env.

  • Disallow unused variables (no-unused-vars)

    Category: Error

    Description: Variables that are declared and not used anywhere in the code might be residues of incomplete refactoring. Retaining these variables takes up space in the code and can lead to confusion.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (payload) { let someUnusedVariable = 0; let anotherUnusedVariable = {}; } };

    Correct Approach

    Don't declare variables that are not going to be used in the code.

  • Disallow eval() (no-eval)

    Category: Error

    Description: Avoid using JavaScript's eval() function, on untrusted code, as it can open a program to several different injection attacks.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (payload) { eval("var a=0;"); } };

    Correct Approach

    Strictly avoid eval().

  • Disallow use of alert (no-alert)

    Category: Error

    Description: JavaScript's alert, confirm, and prompt functions are widely considered to be obtrusive as UI elements and should be replaced by a more appropriate custom UI implementation.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { alert("Don't use me!"); // Some logic }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Correct Approach

    Don't use alert, confirm, or prompt in front-end code. Obtain inputs via forms or Interface API (modals or confirmation dialog) only.

  • Disallow the use of debugger (no-debugger)

    Category: Error

    Description: In the production code, avoid using debugger; as it stops the browser from running code and opens an appropriate debugger.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { // Some logic // "debugger" pauses execution! debugger; }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); });
    EXPAND ↓

    Correct Approach

    Don't use debugger; in production apps.

  • Disallow unreachable code (no-unreachable)

    Category: Error

    Description: The return, throw, break, and continue statements unconditionally exit a block of code and any statements after them cannot be executed. Avoid having unreachable statements.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    exports = { events: [{ event: "onTicketCreate", callback: "onTicketCreateCallback" }], onTicketCreateCallback: function (payload) { console.log("A new ticket has been created!"); return; console.log("Logging arguments from onTicketCreate event: " + JSON.stringify(payload)); // Unreachable as JS stops execution of function when it encounters a 'return' stmt. } }
    EXPAND ↓

    Example of Correct Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    exports = { events: [{ event: "onTicketCreate", callback: "onTicketCreateCallback" }], onTicketCreateCallback: function (payload) { console.log("A new ticket has been created!"); console.log("Logging arguments from onTicketCreate event: " + JSON.stringify(payload)); return; } }
    EXPAND ↓
  • Disallow empty functions (no-empty-function)

    Category: Error

    Description: Empty functions can reduce code readability because readers need to guess whether it is intentional or not. Writing a clear comment for empty functions is good practice.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { thatIsEmpty(); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error } }); }); function thatIsEmpty(){ }
    EXPAND ↓

    Example of Correct Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { thatIsEmpty(); }); }) .catch(function (err) { if (err) { console.error('Error - ', err); //Some logic to handle the error. } }); }); function thatIsEmpty(){ var noLongerEmpty = "Some value"; //Some logic return noLongerEmpty; }
    EXPAND ↓
  • Disallow the use of deprecated functions (no-deprecated-functions)

    Category: Error

    Description: Deprecated functions (which might have existed as a part of v1 of the platform) must not be used in apps. For example, the loadDep and loadDependency functions have been deprecated and are to be replaced with require.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    // 'loadDependency' was used in v1 of the platform and is deprecated as of v2 of the platform. var _ = loadDependency("lodash"); var values = [5, 4, 3, 2, 1]; _.first(values);

    Example of Correct Code

    server.js

    1
    2
    3
    var _ = require("lodash"); var values = [5, 4, 3, 2, 1]; _.first(values);

Third-party resource: ESLint