Lint Validations

When running the fdk validate and fdk pack commands on an app, the Freshworks Developer Kit (FDK) performs linting where the source code is analyzed for possible errors and reported. The rules through which the linter goes through are a collection of best practices pertaining to the Developer Platform which the app must adhere to. Based on the severity category (Warning/Error), violations are displayed which should be corrected before submission.

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

The rules and validations are as follows.

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

    Category: Warning

    Description: When writing asynchronous code, it is possible to create subtle race condition bugs. Variables declared and assigned in different scopes could lead to possible asynchronous race conditions.

    Example of Incorrect Code

    app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var dummyVar = 10; $(document).ready(function () { app.initialized() .then(function (_client) { var client = _client; client.events.on('app.activated', function () { 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 = 10; //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: Promise rejections should always be handled. Forgetting to handle these errors can lead to some really strange behavior in your 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 frontend (no-non-client-request-model)

    Category: Warning

    Description: Request API takes care of CORS and security issues while making REST API calls. REST API calls if not performed via the client.request API in the frontend can compromise security and are susceptible to the CORS issue.

    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: Logging just the error object in promise rejection handlers is not recommended. The error has to be reported (via notifications) to users/agents in layman's terms so that they will be able to undertake the next action.

    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 dependency (no-dependency-mismatch)

    Category: Error

    Description: In serverless applications, there might be dependencies that are declared but left unused. This is not allowed.

    Example of Incorrect Code

    manifest.json

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

    server.js

    1
    2
    3
    4
    5
    6
    7
    8
    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
    { "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
    exports = { events: [ { event: 'onTicketCreate', callback: 'onTicketCreateHandler' }, ], onTicketCreateHandler: function (args) { console.log('Hello ' + args['data']['requester']['name']); } };
  • 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 value 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
    $(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 async programming, a common pitfall while using the callback pattern is nesting callbacks, which makes code more difficult to read the deeper the callbacks are nested. The current depth 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
    $(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 is generally used to deal with asynchronous behavior. This pattern expects an error object or null as the first argument of the callback. Forgetting to handle these errors can lead to some really strange behavior in your 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 have been deprecated in the future versions of JavaScript and their use is forbidden in ECMAScript 5 while 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 through out a project could 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 are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.

    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: JavaScript's eval() function is potentially dangerous and often misused. Using eval() on untrusted code can open a program up to several different injection attacks. Remember, Eval is just one letter away from Evil.

    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 frontend code. Obtain inputs via forms or Interface API (modals or confirmation dialog) only.

  • Disallow the use of debugger (no-debugger)

    Category: Error

    Description: Production code should definitely not contain debugger as it will cause the browser to stop executing code and open 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: Because the return, throw, break, and continue statements unconditionally exit a block of code, any statements after them cannot be executed. Unreachable statements are usually a mistake.

    Example of Incorrect Code

    server.js

    1
    2
    3
    4
    5
    6
    7
    exports = { serverMethod: function(options) { var message = { status: 200, message: "Things went good! " + options.msg }; return; renderData(message); } };

    Example of Correct Code

    server.js

    1
    2
    3
    4
    5
    6
    exports = { serverMethod: function(options) { var message = { status: 200, message: "Things went good! " + options.msg }; renderData(message); } };
  • Disallow empty functions (no-empty-function)

    Category: Error

    Description: Empty functions can reduce readability because readers need to guess whether it's intentional or not. So writing a clear comment for empty functions is a 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 platform v1) must not be used in the app. For example, the functions loadDep and loadDependency 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 Platform. However, deprecated as of V2 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);
  • Disallow empty functions (no-empty-function)

    Category: Error

    Description: Empty functions can reduce readability because readers need to guess whether it's intentional or not. So writing a clear comment for empty functions is a 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 ↓

Third-party resource: ESLint