From 42a3f57e11fcc4f3db6135661a1340a347aa61b4 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Wed, 27 May 2015 17:35:43 +0200 Subject: [PATCH] Added linter for js - use global stricts - use airbnb linter rules - gulp lint target - make target client-lint --- Makefile | 5 + client/.eslintrc | 212 ++++++++++++++++++ client/app/scripts/components/app.js | 2 - client/app/scripts/components/details.js | 2 - client/app/scripts/components/explorer.js | 2 - client/app/scripts/components/groupings.js | 2 - client/app/scripts/components/logo.js | 2 - .../scripts/components/node-details-table.js | 2 - client/app/scripts/components/node-details.js | 2 - client/app/scripts/components/nodes.js | 2 - client/app/scripts/components/status.js | 2 - client/app/scripts/components/topologies.js | 2 - client/app/scripts/constants/topologies.js | 1 - client/app/scripts/main.js | 4 - .../stores/__tests__/app-store-test.js | 1 - client/app/scripts/stores/app-store.js | 1 - client/app/scripts/utils/router-utils.js | 1 - client/gulpfile.js | 134 ++++++----- client/package.json | 6 +- 19 files changed, 296 insertions(+), 89 deletions(-) create mode 100644 client/.eslintrc diff --git a/Makefile b/Makefile index c5dcc48cb..a3d17c501 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,11 @@ client-test: client/test/* -v $(shell pwd)/client/test:/home/weave/test \ $(SCOPE_UI_BUILD_IMAGE) npm test +client-lint: + docker run -ti -v $(shell pwd)/client/app:/home/weave/app \ + -v $(shell pwd)/client/test:/home/weave/test \ + $(SCOPE_UI_BUILD_IMAGE) npm run lint + $(SCOPE_UI_BUILD_EXPORT): client/Dockerfile client/gulpfile.js client/package.json docker build -t $(SCOPE_UI_BUILD_IMAGE) client docker save $(SCOPE_UI_BUILD_IMAGE):latest > $@ diff --git a/client/.eslintrc b/client/.eslintrc new file mode 100644 index 000000000..3b842c853 --- /dev/null +++ b/client/.eslintrc @@ -0,0 +1,212 @@ +{ + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "env": { + "browser": true, + "node": true + }, + "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "forOf": true, + "generators": false, + "modules": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": false, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "spread": true, + "superInFunctions": true, + "templateStrings": true, + "jsx": true + }, + "rules": { +/** + * Strict mode + */ + // babel inserts "use strict"; for us + // http://eslint.org/docs/rules/strict + "strict": [2, "never"], + +/** + * ES6 + */ + "no-var": 2, // http://eslint.org/docs/rules/no-var + +/** + * Variables + */ + "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow + "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names + "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars + "vars": "local", + "args": "after-used" + }], + "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define + +/** + * Possible errors + */ + "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle + "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign + "no-console": 1, // http://eslint.org/docs/rules/no-console + "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger + "no-alert": 1, // http://eslint.org/docs/rules/no-alert + "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition + "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys + "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case + "no-empty": 2, // http://eslint.org/docs/rules/no-empty + "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign + "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast + "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi + "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign + "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations + "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp + "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace + "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls + "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys + "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays + "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable + "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan + "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var + +/** + * Best practices + */ + "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return + "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly + "default-case": 2, // http://eslint.org/docs/rules/default-case + "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation + "allowKeywords": true + }], + "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq + "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in + "no-caller": 2, // http://eslint.org/docs/rules/no-caller + "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return + "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null + "no-eval": 2, // http://eslint.org/docs/rules/no-eval + "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native + "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind + "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough + "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal + "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval + "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks + "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func + "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str + "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign + "no-new": 2, // http://eslint.org/docs/rules/no-new + "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func + "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers + "no-octal": 2, // http://eslint.org/docs/rules/no-octal + "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape + "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign + "no-proto": 2, // http://eslint.org/docs/rules/no-proto + "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare + "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign + "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url + "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare + "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences + "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal + "no-with": 2, // http://eslint.org/docs/rules/no-with + "radix": 2, // http://eslint.org/docs/rules/radix + "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top + "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife + "yoda": 2, // http://eslint.org/docs/rules/yoda + +/** + * Style + */ + "indent": [2, 2], // http://eslint.org/docs/rules/ + "brace-style": [2, // http://eslint.org/docs/rules/brace-style + "1tbs", { + "allowSingleLine": true + }], + "quotes": [ + 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes + ], + "camelcase": [2, { // http://eslint.org/docs/rules/camelcase + "properties": "never" + }], + "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing + "before": false, + "after": true + }], + "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style + "eol-last": 2, // http://eslint.org/docs/rules/eol-last + "func-names": 1, // http://eslint.org/docs/rules/func-names + "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing + "beforeColon": false, + "afterColon": true + }], + "new-cap": [2, { // http://eslint.org/docs/rules/new-cap + "newIsCap": true + }], + "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines + "max": 2 + }], + "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary + "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object + "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func + "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces + "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func + "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle + "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var + "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks + "semi": [2, "always"], // http://eslint.org/docs/rules/semi + "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing + "before": false, + "after": true + }], + "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords + "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks + "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren + "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops + "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case + "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment + +/** + * JSX style + */ + "react/display-name": 0, + "react/jsx-boolean-value": 2, + "react/jsx-quotes": [2, "double"], + "react/jsx-no-undef": 2, + "react/jsx-sort-props": 0, + "react/jsx-sort-prop-types": 0, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/no-did-mount-set-state": [2, "allow-in-func"], + "react/no-did-update-set-state": 2, + "react/no-multi-comp": 2, + "react/no-unknown-property": 2, + "react/prop-types": 2, + "react/react-in-jsx-scope": 2, + "react/self-closing-comp": 2, + "react/wrap-multilines": 2, + "react/sort-comp": [2, { + "order": [ + "displayName", + "mixins", + "statics", + "propTypes", + "getDefaultProps", + "getInitialState", + "componentWillMount", + "componentDidMount", + "componentWillReceiveProps", + "shouldComponentUpdate", + "componentWillUpdate", + "componentWillUnmount", + "/^on.+$/", + "/^get.+$/", + "/^render.+$/", + "render" + ] + }] + } +} diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index 9f3d4e972..7d9f293df 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/components/details.js b/client/app/scripts/components/details.js index 161794578..804bd36b5 100644 --- a/client/app/scripts/components/details.js +++ b/client/app/scripts/components/details.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); var mui = require('material-ui'); diff --git a/client/app/scripts/components/explorer.js b/client/app/scripts/components/explorer.js index 84dd62f1e..522222190 100644 --- a/client/app/scripts/components/explorer.js +++ b/client/app/scripts/components/explorer.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/components/groupings.js b/client/app/scripts/components/groupings.js index f6e8eeed7..2d8e61671 100644 --- a/client/app/scripts/components/groupings.js +++ b/client/app/scripts/components/groupings.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/components/logo.js b/client/app/scripts/components/logo.js index bc3cf2b39..93b785ccc 100644 --- a/client/app/scripts/components/logo.js +++ b/client/app/scripts/components/logo.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var Logo = React.createClass({ diff --git a/client/app/scripts/components/node-details-table.js b/client/app/scripts/components/node-details-table.js index 5ef29f6a3..6e280781b 100644 --- a/client/app/scripts/components/node-details-table.js +++ b/client/app/scripts/components/node-details-table.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js index 628755803..cb58bb5d5 100644 --- a/client/app/scripts/components/node-details.js +++ b/client/app/scripts/components/node-details.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js index 3b211f6c1..5b7923fba 100644 --- a/client/app/scripts/components/nodes.js +++ b/client/app/scripts/components/nodes.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var NodesChart = require('../charts/nodes-chart'); diff --git a/client/app/scripts/components/status.js b/client/app/scripts/components/status.js index 74005357d..42a7e2657 100644 --- a/client/app/scripts/components/status.js +++ b/client/app/scripts/components/status.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var Status = React.createClass({ diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js index 4e4db8913..8c8f0255f 100644 --- a/client/app/scripts/components/topologies.js +++ b/client/app/scripts/components/topologies.js @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - var React = require('react'); var _ = require('lodash'); diff --git a/client/app/scripts/constants/topologies.js b/client/app/scripts/constants/topologies.js index 38c920fb5..b8180ef16 100644 --- a/client/app/scripts/constants/topologies.js +++ b/client/app/scripts/constants/topologies.js @@ -1,4 +1,3 @@ - module.exports = [ { url: '/api/topology/applications', diff --git a/client/app/scripts/main.js b/client/app/scripts/main.js index 8085367af..90c11851e 100644 --- a/client/app/scripts/main.js +++ b/client/app/scripts/main.js @@ -1,7 +1,3 @@ -/** - * @jsx React.DOM - */ - var React = require('react'); var App = require('./components/app.js'); diff --git a/client/app/scripts/stores/__tests__/app-store-test.js b/client/app/scripts/stores/__tests__/app-store-test.js index 9e30e5bd3..e9cae6ce9 100644 --- a/client/app/scripts/stores/__tests__/app-store-test.js +++ b/client/app/scripts/stores/__tests__/app-store-test.js @@ -1,4 +1,3 @@ - describe('AppStore', function() { var ActionTypes = require('../../constants/action-types'); diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js index b3d986236..074382459 100644 --- a/client/app/scripts/stores/app-store.js +++ b/client/app/scripts/stores/app-store.js @@ -1,4 +1,3 @@ - var EventEmitter = require('events').EventEmitter; var _ = require('lodash'); var assign = require('object-assign'); diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js index 12d56ef5c..3a3726e05 100644 --- a/client/app/scripts/utils/router-utils.js +++ b/client/app/scripts/utils/router-utils.js @@ -1,4 +1,3 @@ - var page = require('page'); var AppActions = require('../actions/app-actions'); diff --git a/client/gulpfile.js b/client/gulpfile.js index c443f086e..dfe0e806b 100644 --- a/client/gulpfile.js +++ b/client/gulpfile.js @@ -16,100 +16,114 @@ var isDev = true; var isProd = false; gulp.task('styles', function () { - return gulp.src('app/styles/main.less') - .pipe($.if(isDev, $.sourcemaps.init())) - .pipe($.less()) - .pipe($.autoprefixer('last 1 version')) - .pipe($.if(isDev, $.sourcemaps.write())) - .pipe($.if(isDev, gulp.dest('.tmp/styles'))) - .pipe($.if(isProd, $.csso())) - .pipe($.if(isProd, gulp.dest('dist/styles'))) - .pipe($.size()) - .pipe(livereload()); + return gulp.src('app/styles/main.less') + .pipe($.if(isDev, $.sourcemaps.init())) + .pipe($.less()) + .pipe($.autoprefixer('last 1 version')) + .pipe($.if(isDev, $.sourcemaps.write())) + .pipe($.if(isDev, gulp.dest('.tmp/styles'))) + .pipe($.if(isProd, $.csso())) + .pipe($.if(isProd, gulp.dest('dist/styles'))) + .pipe($.size()) + .pipe(livereload()); }); gulp.task('scripts', function() { - var bundler = browserify('./app/scripts/main.js', {debug: isDev}); - bundler.transform(reactify); + var bundler = browserify('./app/scripts/main.js', {debug: isDev}); + bundler.transform(reactify); - var stream = bundler.bundle(); + var stream = bundler.bundle(); - return stream - .pipe(source('bundle.js')) - .pipe($.if(isDev, gulp.dest('.tmp/scripts'))) - .pipe($.if(isProd, buffer())) - .pipe($.if(isProd, $.uglify())) - .pipe($.if(isProd, gulp.dest('dist/scripts'))) - .pipe(livereload()) - .on('error', $.util.log); + return stream + .pipe(source('bundle.js')) + .pipe($.if(isDev, gulp.dest('.tmp/scripts'))) + .pipe($.if(isProd, buffer())) + .pipe($.if(isProd, $.uglify())) + .pipe($.if(isProd, gulp.dest('dist/scripts'))) + .pipe(livereload()) + .on('error', $.util.log); }); gulp.task('html', ['styles', 'scripts'], function () { - return gulp.src('app/*.html') - .pipe($.preprocess()) - .pipe(gulp.dest('dist')) - .pipe($.size()) - .pipe(livereload()); + return gulp.src('app/*.html') + .pipe($.preprocess()) + .pipe(gulp.dest('dist')) + .pipe($.size()) + .pipe(livereload()); }); gulp.task('images', function () { - return gulp.src('app/images/**/*') - .pipe(gulp.dest('dist/images')) - .pipe($.size()); + return gulp.src('app/images/**/*') + .pipe(gulp.dest('dist/images')) + .pipe($.size()); }); gulp.task('fonts', function () { - return gulp.src('node_modules/font-awesome/fonts/*') - .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) - .pipe($.flatten()) - .pipe($.if(isDev, gulp.dest('.tmp/fonts'))) - .pipe($.if(isProd, gulp.dest('dist/fonts'))) - .pipe($.size()); + return gulp.src('node_modules/font-awesome/fonts/*') + .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) + .pipe($.flatten()) + .pipe($.if(isDev, gulp.dest('.tmp/fonts'))) + .pipe($.if(isProd, gulp.dest('dist/fonts'))) + .pipe($.size()); }); gulp.task('extras', function () { - return gulp.src(['app/*.*', '!app/*.html'], { dot: true }) - .pipe(gulp.dest('dist')); + return gulp.src(['app/*.*', '!app/*.html'], { dot: true }) + .pipe(gulp.dest('dist')); }); gulp.task('clean', function () { - return del(['.tmp', 'dist']); + return del(['.tmp', 'dist']); +}); + + +gulp.task('lint', function () { + return gulp.src(['app/**/*.js']) + // eslint() attaches the lint output to the eslint property + // of the file object so it can be used by other modules. + .pipe($.eslint()) + // eslint.format() outputs the lint results to the console. + // Alternatively use eslint.formatEach() (see Docs). + .pipe($.eslint.format()); + // To have the process exit with an error code (1) on + // lint error, return the stream and pipe to failOnError last. + //.pipe($.eslint.failOnError()); }); gulp.task('production', ['html', 'images', 'fonts', 'extras']); gulp.task('build', function () { - isDev = false; - isProd = true; - gulp.start('production'); + isDev = false; + isProd = true; + gulp.start('production'); }); gulp.task('default', ['clean'], function () { - gulp.start('build'); + gulp.start('build'); }); gulp.task('connect', function () { - connect.server({ - root: ['.tmp', 'app'], - port: 4041, - middleware: function(connect, o) { - return [(function() { - var url = require('url'); - var proxy = require('proxy-middleware'); - var options = url.parse('http://localhost:4040/api'); - options.route = '/api'; - return proxy(options); - })()]; - }, - livereload: false - }); + connect.server({ + root: ['.tmp', 'app'], + port: 4041, + middleware: function(connect, o) { + return [(function() { + var url = require('url'); + var proxy = require('proxy-middleware'); + var options = url.parse('http://localhost:4040/api'); + options.route = '/api'; + return proxy(options); + })()]; + }, + livereload: false + }); }); gulp.task('serve', ['connect', 'styles', 'scripts', 'fonts']); gulp.task('watch', ['serve'], function () { - livereload.listen(); - gulp.watch('app/styles/**/*.less', ['styles']); - gulp.watch('app/scripts/**/*.js', ['scripts']); - gulp.watch('app/images/**/*', ['images']); + livereload.listen(); + gulp.watch('app/styles/**/*.less', ['styles']); + gulp.watch('app/scripts/**/*.js', ['scripts']); + gulp.watch('app/images/**/*', ['images']); }); diff --git a/client/package.json b/client/package.json index 7b14a29f5..389854a93 100644 --- a/client/package.json +++ b/client/package.json @@ -21,16 +21,19 @@ "reqwest": "~1.1.5" }, "devDependencies": { + "babel-eslint": "^3.1.9", "browserify": "^10.2.0", "del": "^1.1.1", + "eslint": "^0.21.2", + "eslint-plugin-react": "^2.3.0", "gulp": "^3.8.11", "gulp-autoprefixer": "^2.3.0", "gulp-connect": "^2.2.0", "gulp-csso": "^1.0.0", + "gulp-eslint": "^0.12.0", "gulp-filter": "^2.0.2", "gulp-flatten": "^0.0.4", "gulp-if": "^1.2.5", - "gulp-jshint": "^1.10.0", "gulp-less": "^3.0.3", "gulp-livereload": "^3.8.0", "gulp-load-plugins": "^0.10.0", @@ -55,6 +58,7 @@ "vinyl-source-stream": "^1.1.0" }, "scripts": { + "lint": "gulp lint", "start": "gulp", "test": "karma start test/karma.conf.js --single-run" },