Enforcing coding standards in a JS project

Enforcing coding standards in a JS project

Talk given at JSConf Budapest (Budapest, Hungary) - May 2015

Transcript

  1. 1.

    ENFORCING 
 CODING 
 STANDARDS 
 in a JS PROJECT

    Sebastiano Armeli @sebarmeli 14/05/2015 - JSConf BD
  2. 2.

    to enforce verb (used with object), enforced, enforcing. to put

    or keep in force; to compel obedience to: “to enforce a rule; Traffic laws will be strictly enforced.”
  3. 3.
  4. 4.
  5. 5.

    standard noun a rule or principle that is used as

    a basis for judgment: “They tried to establish standards for a new approach.”
  6. 6.
  7. 7.

    commit 111111 Author: Sebastiano Armeli Date: Sun Dec 21 22:08:00

    2014 -0500 adding something commit 2222222 Author: Sebastiano Armeli Date: Thu Dec 18 15:35:39 2014 -0500 it will work, trust me
  8. 9.
  9. 13.
  10. 14.
  11. 15.

    ! IDE (Editorconfig) ! Quality & Style tools (JSHint, JSCS,

    ESLint) ! Git commits standards ! Build tools (Grunt, Gulp) ! Language - Transpiler (ES6 - Babel) ! Complexity tool (Plato) Summary 1/2
  12. 16.

    ! Testing (Mocha, Karma) ! Automated Release Flow (Jenkins, NPM)

    ! Setup script ! Documentation Summary 2/2
  13. 17.
  14. 18.

    root = true [*] indent_style = space indent_size = 2

    end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true .editorconfig
  15. 23.
  16. 24.

    { "curly": true, "eqeqeq": false, "latedef": true, "newcap": true, "noarg":

    true, "sub": true, "boss": true, "indent": 2, "noempty": true, "expr": true, "eqnull": true, "esnext": true, "browser": true, "white": true, "undef": true, "predef": [ “require”, "module", “exports", "CustomEvent"] } .jshintrc
  17. 25.

    { "curly": true, "eqeqeq": false, "latedef": true, "newcap": true, "noarg":

    true, "sub": true, "boss": true, "indent": 2, "noempty": true, "expr": true, "eqnull": true, "esnext": true, "browser": true, "white": true, "undef": true, "predef": [ “require”, "module", “exports", "CustomEvent"] } .jshintrc
  18. 26.

    { "curly": true, "eqeqeq": false, "latedef": true, "newcap": true, "noarg":

    true, "sub": true, "boss": true, "indent": 2, "noempty": true, "expr": true, "eqnull": true, "esnext": true, "browser": true, "white": true, "undef": true, "predef": [ “require”, "module", “exports", "CustomEvent"] } .jshintrc
  19. 27.
  20. 28.

    { "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try"],

    "disallowImplicitTypeConversion": ["string"], "disallowMultipleLineBreaks": true, "disallowMixedSpacesAndTabs": true, "disallowKeywords": ["with"], "disallowMultipleVarDecl": true, "disallowTrailingComma": true, "disallowTrailingWhitespace": true, "maximumLineLength": 80, "esnext": true } .jscsrc
  21. 29.

    { "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try"],

    "disallowImplicitTypeConversion": ["string"], "disallowMultipleLineBreaks": true, "disallowMixedSpacesAndTabs": true, "disallowKeywords": ["with"], "disallowMultipleVarDecl": true, "disallowTrailingComma": true, "disallowTrailingWhitespace": true, "maximumLineLength": 80, "esnext": true } .jscsrc
  22. 30.

    { "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try"],

    "disallowImplicitTypeConversion": ["string"], "disallowMultipleLineBreaks": true, "disallowMixedSpacesAndTabs": true, "disallowKeywords": ["with"], "disallowMultipleVarDecl": true, "disallowTrailingComma": true, "disallowTrailingWhitespace": true, "maximumLineLength": 80, "esnext": true } .jscsrc
  23. 31.
  24. 33.

    rules: space-before-blocks: 2 eqeqeq: [2, 'smart'] curly: [2, 'multi-line'] quotes:

    [2, 'single'] space-after-keywords: 2 no-unused-vars: [2, args: none] no-comma-dangle: 2 no-unused-expressions: 0 no-multi-spaces: 2 …
  25. 34.

    rules: space-before-blocks: 2 eqeqeq: [2, 'smart'] curly: [2, 'multi-line'] quotes:

    [2, 'single'] space-after-keywords: 2 no-unused-vars: [2, args: none] no-comma-dangle: 2 no-unused-expressions: 0 no-multi-spaces: 2 …
  26. 35.

    no-unused-vars: [2, args: none] function test1(a, b) { var c,

    d = 2; return a + d; } test1(1, 2); Error!! function test2(a, b, c) { return a + b; } test2(1, 2); Ok
  27. 37.

    "use strict”; module.exports = function(context) { return { "NewExpression": function(node)

    { if (node.callee.name === "Object") { context.report(node, “Error …”); } } }; }; no-new-object.js let obj = new Object(); let obj = {};
  28. 38.

    "use strict”; module.exports = function(context) { return { "NewExpression": function(node)

    { if (node.callee.name === "Object") { context.report(node, “Error …”); } } }; }; no-new-object.js let obj = {};
  29. 39.
  30. 40.

    Git Commits (feat | fix | docs | style |

    refactor | test | chore)(<scope>): <description> E.g. doc(readme): update with additional links.
  31. 41.

    Changelog conventional-changelog Changelog.md commit 7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Author: Sebastiano Armeli <xxx@yyy.com> Date:

    Tue Jan 6 11:48:59 2015 -0500 refactor(BaseAd): Removed addToStreamTime method from BaseAd commit 7bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb Author: Sebastiano Armeli <xxx@yyy.com> Date: Tue Jan 6 00:04:49 2015 -0500 style(gpt): rearrange for better readability
  32. 44.

    var gulp = require('gulp'); var plugins = require(‘gulp-load-plugins')(); … gulp.task('eslint',

    function() { return gulp.src(['src/**/*.js']) .pipe(plugins.eslint()) .pipe(plugins.eslint.format()) .pipe(plugins.eslint.failOnError()); }); … gulpfile.js
  33. 45.
  34. 48.
  35. 49.

    describe('#_onContainerResume', function() { it('should call play when container resumes', function()

    { videoAd.views.set(mockedVideoAdMetadata.id, { play: function() {}, hasBeenPlayed: true }); sinon.stub(videoAd.views.get(mockedVideoAdMetadata.id), 'play'); videoAd._onContainerResume(); expect(videoAd.views.get(mockedVideoAdMetadata.id).play).to.have .been.called; }); });
  36. 50.

    module.exports = function(config) { config.set({ basePath: '../', frameworks: ['mocha', 'fixture'],

    files: [ …. ], browsers: ‘Chrome’, singleRun: false, preprocessors: { '**/*.html': ['html2js'], '**/*.json': ['html2js'] }, sauceLabs: { … } }); }; karma.conf.js
  37. 51.

    module.exports = function(config) { config.set({ basePath: '../', frameworks: ['mocha', 'fixture'],

    files: [ …. ], browsers: ‘Chrome’, singleRun: false, preprocessors: { '**/*.html': ['html2js'], '**/*.json': ['html2js'] }, sauceLabs: { … } }); }; karma.conf.js
  38. 52.

    module.exports = function(config) { config.set({ basePath: '../', frameworks: ['mocha', 'fixture'],

    files: [ …. ], browsers: ‘Chrome’, singleRun: false, preprocessors: { '**/*.html': ['html2js'], '**/*.json': ['html2js'] }, sauceLabs: { … } }); }; karma.conf.js
  39. 57.
  40. 58.

    Sebastiano Armeli <…@spotify.com> Brice Lin <..@spotify.com> <…@gmail.com> Jason Palmer <..@spotify.com>

    <..@spotify.com> Joseph Werle <..@spotify.com> <…@gmail.com> Olof Kihlberg <…@spotify.com> Sigfrido Chirinos <…@spotify.com> .mailmap
  41. 59.