From 777ef3fe905d4ca55456d9fa21284ace007c38d1 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 18 Sep 2017 17:31:06 -0400 Subject: [PATCH] Add more linter rules and de-lint --- awx/ui/.eslintignore | 1 - awx/ui/.eslintrc.js | 28 ++-- awx/ui/build/webpack.base.js | 43 ++++-- awx/ui/build/webpack.development.js | 23 +-- awx/ui/build/webpack.production.js | 17 ++- awx/ui/build/webpack.test.js | 6 +- awx/ui/build/webpack.watch.js | 17 ++- .../credentials/legacy.credentials.js | 138 ++++++++++-------- .../lib/components/input/base.controller.js | 16 +- .../input/textarea-secret.directive.js | 38 ++--- .../layout/side-nav-item.directive.js | 12 +- awx/ui/client/lib/models/Me.js | 2 +- awx/ui/client/src/vendor.js | 1 + awx/ui/package.json | 3 +- 14 files changed, 186 insertions(+), 159 deletions(-) diff --git a/awx/ui/.eslintignore b/awx/ui/.eslintignore index 238ed7c2db..1de8506d67 100644 --- a/awx/ui/.eslintignore +++ b/awx/ui/.eslintignore @@ -16,5 +16,4 @@ test !client/lib/components/**/*.js !client/lib/models/**/*.js !client/lib/services/**/*.js - !client/features/**/*.js diff --git a/awx/ui/.eslintrc.js b/awx/ui/.eslintrc.js index a1db451799..5cb4504be4 100644 --- a/awx/ui/.eslintrc.js +++ b/awx/ui/.eslintrc.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { + root: true, extends: [ 'airbnb-base' ], @@ -27,15 +28,22 @@ module.exports = { jsyaml: true }, rules: { - indent: [0, 4], - 'comma-dangle': 0, - 'space-before-function-paren': [2, 'always'], - 'arrow-parens': 0, - 'no-param-reassign': 0, - 'no-underscore-dangle': 0, - 'no-mixed-operators': 0, - 'no-plusplus': 0, - 'no-continue': 0, - 'object-curly-newline': 0 + 'arrow-parens': 'off', + 'comma-dangle': 'off', + indent: ['error', 4, { + SwitchCase: 1 + }], + 'max-len': ['error', { + code: 100, + ignoreStrings: true, + ignoreTemplateLiterals: true, + }], + 'no-continue': 'off', + 'no-mixed-operators': 'off', + 'no-param-reassign': 'off', + 'no-plusplus': 'off', + 'no-underscore-dangle': 'off', + 'object-curly-newline': 'off', + 'space-before-function-paren': ['error', 'always'] } }; diff --git a/awx/ui/build/webpack.base.js b/awx/ui/build/webpack.base.js index f39134f570..dbdec3eafb 100644 --- a/awx/ui/build/webpack.base.js +++ b/awx/ui/build/webpack.base.js @@ -3,8 +3,8 @@ const path = require('path'); const webpack = require('webpack'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); const CLIENT_PATH = path.resolve(__dirname, '../client'); const LIB_PATH = path.join(CLIENT_PATH, 'lib'); @@ -20,6 +20,7 @@ const NODE_MODULES_PATH = path.join(UI_PATH, 'node_modules'); const SERVICES_PATH = path.join(LIB_PATH, 'services'); const SOURCE_PATH = path.join(CLIENT_PATH, 'src'); const STATIC_PATH = path.join(UI_PATH, 'static'); +const THEME_PATH = path.join(LIB_PATH, 'theme'); const APP_ENTRY = path.join(SOURCE_PATH, 'app.js'); const VENDOR_ENTRY = path.join(SOURCE_PATH, 'vendor.js'); @@ -32,7 +33,7 @@ const CHUNKS = ['vendor', 'app']; const VENDOR = VENDOR_ENTRY; const APP = [THEME_ENTRY, APP_ENTRY]; -let base = { +const base = { entry: { vendor: VENDOR, app: APP @@ -42,7 +43,17 @@ let base = { publicPath: '', filename: OUTPUT }, - stats: 'minimal', + stats: { + children: false, + modules: false, + chunks: false, + excludeAssets: name => { + const chunkNames = `(${CHUNKS.join('|')})`; + const outputPattern = new RegExp(`${chunkNames}\.[a-f0-9]+\.(js|css)$`, 'i'); + + return !outputPattern.test(name); + } + }, module: { rules: [ { @@ -50,7 +61,13 @@ let base = { loader: 'babel-loader', exclude: /node_modules/, options: { - presets: ['env'] + presets: [ + ['env', { + targets: { + browsers: ['last 2 versions'] + } + }] + ] } }, { @@ -81,10 +98,6 @@ let base = { /node_modules\/d3\/d3\.min.js$/ ] }, - { - test: /\.js$/, - use: 'imports-loader?define=>false' - }, { test: /\.html$/, use: ['ngtemplate-loader', 'html-loader'], @@ -169,11 +182,12 @@ let base = { filename: INDEX_OUTPUT, inject: false, chunks: CHUNKS, - chunksSortMode: (moduleA, moduleB) => { - moduleA.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) - moduleB.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1) + chunksSortMode: (chunk) => { + if (chunk.names[0] === 'polyfill' || chunk.names[0] === 'vendor') { + return -1; + } - return moduleA.names[0] === 'vendor' ? -1 : 1 + return 1; } }) ], @@ -183,12 +197,13 @@ let base = { '~models': MODELS_PATH, '~services': SERVICES_PATH, '~components': COMPONENTS_PATH, + '~theme': THEME_PATH, '~modules': NODE_MODULES_PATH, '~assets': ASSETS_PATH, - 'd3$': '~modules/d3/d3.min.js', + d3$: '~modules/d3/d3.min.js', 'codemirror.jsonlint$': '~modules/codemirror/addon/lint/json-lint.js', 'jquery-resize$': '~modules/javascript-detect-element-resize/jquery.resize.js', - 'select2$': '~modules/select2/dist/js/select2.full.min.js', + select2$: '~modules/select2/dist/js/select2.full.min.js', 'js-yaml$': '~modules/js-yaml/dist/js-yaml.min.js', 'lr-infinite-scroll$': '~modules/lr-infinite-scroll/lrInfiniteScroll.js', 'angular-ui-router$': '~modules/angular-ui-router/release/angular-ui-router.js', diff --git a/awx/ui/build/webpack.development.js b/awx/ui/build/webpack.development.js index 4a66c0e6de..2d85a4bd7a 100644 --- a/awx/ui/build/webpack.development.js +++ b/awx/ui/build/webpack.development.js @@ -2,27 +2,10 @@ const path = require('path'); const _ = require('lodash'); -const ESLINTRC_PATH = path.resolve(__dirname, '..', '.eslintrc.js'); -const LINTED_PATHS = [ - /.js$/ -]; +const base = require('./webpack.base'); -let base = require('./webpack.base'); - -let development = { - devtool: 'cheap-source-map', - module: { - rules: [ - { - test: /\.js$/, - enforce: 'pre', - exclude: /node_modules/, - loader: 'eslint-loader' - } - ] - } +const development = { + devtool: 'cheap-source-map' }; -development.module.rules = base.module.rules.concat(development.module.rules) - module.exports = _.merge(base, development); diff --git a/awx/ui/build/webpack.production.js b/awx/ui/build/webpack.production.js index 80955f7fe0..e709962765 100644 --- a/awx/ui/build/webpack.production.js +++ b/awx/ui/build/webpack.production.js @@ -5,15 +5,15 @@ const webpack = require('webpack'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -let base = require('./webpack.base'); +const base = require('./webpack.base'); const CLIENT_PATH = path.resolve(__dirname, '../client'); const UI_PATH = path.resolve(__dirname, '..'); -const INSTALL_RUNNING_ENTRY = path.join(CLIENT_PATH, 'installing.template.ejs') +const INSTALL_RUNNING_ENTRY = path.join(CLIENT_PATH, 'installing.template.ejs'); const INSTALL_RUNNING_OUTPUT = path.join(UI_PATH, 'templates/ui/installing.html'); const CHUNKS = ['vendor', 'app']; -let production = { +const production = { plugins: [ new UglifyJSPlugin({ compress: true, @@ -25,16 +25,17 @@ let production = { filename: INSTALL_RUNNING_OUTPUT, inject: false, chunks: CHUNKS, - chunksSortMode: (moduleA, moduleB) => { - moduleA.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1); - moduleB.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1); + chunksSortMode: (chunk) => { + if (chunk.names[0] === 'polyfill' || chunk.names[0] === 'vendor') { + return -1; + } - return moduleA.names[0] === 'vendor' ? -1 : 1; + return 1; } }) ] }; -production.plugins = base.plugins.concat(production.plugins) +production.plugins = base.plugins.concat(production.plugins); module.exports = _.merge(base, production); diff --git a/awx/ui/build/webpack.test.js b/awx/ui/build/webpack.test.js index e23b2a957a..0ebcac286b 100644 --- a/awx/ui/build/webpack.test.js +++ b/awx/ui/build/webpack.test.js @@ -5,9 +5,9 @@ const webpack = require('webpack'); const STATIC_URL = '/static/'; -let development = require('./webpack.development'); +const development = require('./webpack.development'); -let test = { +const test = { plugins: [ new webpack.DefinePlugin({ $basePath: STATIC_URL @@ -15,7 +15,7 @@ let test = { ] }; -test.plugins = development.plugins.concat(test.plugins) +test.plugins = development.plugins.concat(test.plugins); module.exports = _.merge(development, test); diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index 92cb6aa4f3..85431adddc 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -8,9 +8,19 @@ const TARGET_PORT = _.get(process.env, 'npm_package_config_django_port', 8043); const TARGET_HOST = _.get(process.env, 'npm_package_config_django_host', 'https://localhost'); const TARGET = `https://${TARGET_HOST}:${TARGET_PORT}`; -let development = require('./webpack.development'); +const development = require('./webpack.development'); -let watch = { +const watch = { + module: { + rules: [ + { + test: /\.js$/, + enforce: 'pre', + exclude: /node_modules/, + loader: 'eslint-loader' + } + ] + }, plugins: [ new HtmlWebpackHarddiskPlugin(), new webpack.HotModuleReplacementPlugin() @@ -36,7 +46,8 @@ let watch = { } }; -watch.plugins = development.plugins.concat(watch.plugins) +watch.module.rules = development.module.rules.concat(watch.module.rules); +watch.plugins = development.plugins.concat(watch.plugins); module.exports = _.merge(development, watch); diff --git a/awx/ui/client/features/credentials/legacy.credentials.js b/awx/ui/client/features/credentials/legacy.credentials.js index 5b2a9a5184..c294207dea 100644 --- a/awx/ui/client/features/credentials/legacy.credentials.js +++ b/awx/ui/client/features/credentials/legacy.credentials.js @@ -48,60 +48,59 @@ function LegacyCredentialsService () { url: '/permissions?{permission_search:queryset}', resolve: { ListDefinition: () => ({ - name: 'permissions', - disabled: 'organization === undefined', - ngClick: 'organization === undefined || $state.go(\'credentials.edit.permissions\')', - awToolTip: '{{permissionsTooltip}}', - dataTipWatch: 'permissionsTooltip', - awToolTipTabEnabledInEditMode: true, - dataPlacement: 'right', - basePath: 'api/v2/credentials/{{$stateParams.id}}/access_list/', - search: { - order_by: 'username' - }, - type: 'collection', - title: N_('Permissions'), - iterator: 'permission', - index: false, - open: false, - actions: { - add: { - ngClick: '$state.go(\'.add\')', - label: 'Add', - awToolTip: N_('Add a permission'), - actionClass: 'btn List-buttonSubmit', - buttonContent: `+ ${N_('ADD')}`, - ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)' - } - }, - fields: { - username: { - key: true, - label: N_('User'), - linkBase: 'users', - class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4' - }, - role: { - label: N_('Role'), - type: 'role', - nosort: true, - class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4' - }, - team_roles: { - label: N_('Team Roles'), - type: 'team_roles', - nosort: true, - class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4' - } + name: 'permissions', + disabled: 'organization === undefined', + ngClick: 'organization === undefined || $state.go(\'credentials.edit.permissions\')', + awToolTip: '{{permissionsTooltip}}', + dataTipWatch: 'permissionsTooltip', + awToolTipTabEnabledInEditMode: true, + dataPlacement: 'right', + basePath: 'api/v2/credentials/{{$stateParams.id}}/access_list/', + search: { + order_by: 'username' + }, + type: 'collection', + title: N_('Permissions'), + iterator: 'permission', + index: false, + open: false, + actions: { + add: { + ngClick: '$state.go(\'.add\')', + label: 'Add', + awToolTip: N_('Add a permission'), + actionClass: 'btn List-buttonSubmit', + buttonContent: `+ ${N_('ADD')}`, + ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)' + } + }, + fields: { + username: { + key: true, + label: N_('User'), + linkBase: 'users', + class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4' + }, + role: { + label: N_('Role'), + type: 'role', + nosort: true, + class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4' + }, + team_roles: { + label: N_('Team Roles'), + type: 'team_roles', + nosort: true, + class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4' } - }), - Dataset: ['QuerySet', '$stateParams', (qs, $stateParams) => { - const id = $stateParams.credential_id; - const path = `api/v2/credentials/${id}/access_list/`; - - return qs.search(path, $stateParams.permission_search); } - ] + }), + Dataset: ['QuerySet', '$stateParams', (qs, $stateParams) => { + const id = $stateParams.credential_id; + const path = `api/v2/credentials/${id}/access_list/`; + + return qs.search(path, $stateParams.permission_search); + }] }, params: { permission_search: { @@ -144,7 +143,14 @@ function LegacyCredentialsService () { 'GetBasePath', 'resourceData', (list, qs, $stateParams, GetBasePath, resourceData) => { - const path = resourceData.data.organization ? `${GetBasePath('organizations')}${resourceData.data.organization}/users` : ((list.basePath) || GetBasePath(list.name)); + let path; + + if (resourceData.data.organization) { + path = `${GetBasePath('organizations')}${resourceData.data.organization}/users`; + } else { + path = list.basePath || GetBasePath(list.name); + } + return qs.search(path, $stateParams.user_search); } ], @@ -156,17 +162,21 @@ function LegacyCredentialsService () { 'resourceData', (list, qs, $stateParams, GetBasePath, resourceData) => { const path = GetBasePath(list.basePath) || GetBasePath(list.name); + const org = resourceData.data.organization; - if (!resourceData.data.organization) { + if (!org) { return null; } - $stateParams[`${list.iterator}_search`].organization = resourceData.data.organization; - return qs.search(path, $stateParams.team_search); + $stateParams[`${list.iterator}_search`].organization = org; + + return qs.search(path, $stateParams.team_search); } ], - resourceData: ['CredentialModel', '$stateParams', (Credential, $stateParams) => new Credential('get', $stateParams.credential_id) - .then(credential => ({ data: credential.get() }))], + resourceData: ['CredentialModel', '$stateParams', (Credential, $stateParams) => + new Credential('get', $stateParams.credential_id) + .then(credential => ({ data: credential.get() })) + ] }, params: { user_search: { @@ -246,9 +256,9 @@ function LegacyCredentialsService () { ListDefinition: ['OrganizationList', list => list], Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => qs.search( - GetBasePath('organizations'), - $stateParams[`${list.iterator}_search`] - ) + GetBasePath('organizations'), + $stateParams[`${list.iterator}_search`] + ) ] }, onExit ($state) { @@ -285,9 +295,9 @@ function LegacyCredentialsService () { ListDefinition: ['CredentialTypesList', list => list], Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => qs.search( - GetBasePath('credential_types'), - $stateParams[`${list.iterator}_search`] - ) + GetBasePath('credential_types'), + $stateParams[`${list.iterator}_search`] + ) ] }, onExit ($state) { diff --git a/awx/ui/client/lib/components/input/base.controller.js b/awx/ui/client/lib/components/input/base.controller.js index 24d2867cad..13fa825a5d 100644 --- a/awx/ui/client/lib/components/input/base.controller.js +++ b/awx/ui/client/lib/components/input/base.controller.js @@ -112,14 +112,14 @@ function BaseInputController (strings) { scope.state._disabled = true; scope.state._enableToggle = false; } else if (scope.state._isBeingReplaced === false) { - scope.state._disabled = true; - scope.state._enableToggle = true; - scope.state._value = scope.state._preEditValue; - } else { - scope.state._activeModel = '_value'; - scope.state._disabled = false; - scope.state._value = ''; - } + scope.state._disabled = true; + scope.state._enableToggle = true; + scope.state._value = scope.state._preEditValue; + } else { + scope.state._activeModel = '_value'; + scope.state._disabled = false; + scope.state._value = ''; + } vm.check(); }; diff --git a/awx/ui/client/lib/components/input/textarea-secret.directive.js b/awx/ui/client/lib/components/input/textarea-secret.directive.js index dba34cee13..7e4cd964d6 100644 --- a/awx/ui/client/lib/components/input/textarea-secret.directive.js +++ b/awx/ui/client/lib/components/input/textarea-secret.directive.js @@ -33,9 +33,9 @@ function AtInputTextareaSecretController (baseInputController, eventService) { scope.state._buttonText = vm.strings.get('REPLACE'); scope.state._placeholder = vm.strings.get('ENCRYPTED'); } else if (scope.state.format === 'ssh_private_key') { - vm.listeners = vm.setFileListeners(textarea, input); - scope.state._displayHint = true; - } + vm.listeners = vm.setFileListeners(textarea, input); + scope.state._displayHint = true; + } vm.check(); }; @@ -55,25 +55,25 @@ function AtInputTextareaSecretController (baseInputController, eventService) { }; vm.setFileListeners = (textareaEl, inputEl) => eventService.addListeners([ - [textareaEl, 'dragenter', event => { - event.stopPropagation(); - event.preventDefault(); - scope.$apply(() => { scope.drag = true; }); - }], + [textareaEl, 'dragenter', event => { + event.stopPropagation(); + event.preventDefault(); + scope.$apply(() => { scope.drag = true; }); + }], - [inputEl, 'dragleave', event => { - event.stopPropagation(); - event.preventDefault(); - scope.$apply(() => { scope.drag = false; }); - }], + [inputEl, 'dragleave', event => { + event.stopPropagation(); + event.preventDefault(); + scope.$apply(() => { scope.drag = false; }); + }], - [inputEl, 'change', event => { - const reader = new FileReader(); + [inputEl, 'change', event => { + const reader = new FileReader(); - reader.onload = () => vm.readFile(reader, event); - reader.readAsText(inputEl.files[0]); - }] - ]); + reader.onload = () => vm.readFile(reader, event); + reader.readAsText(inputEl.files[0]); + }] + ]); vm.readFile = (reader) => { scope.$apply(() => { diff --git a/awx/ui/client/lib/components/layout/side-nav-item.directive.js b/awx/ui/client/lib/components/layout/side-nav-item.directive.js index e7fb2d9231..ab52d74964 100644 --- a/awx/ui/client/lib/components/layout/side-nav-item.directive.js +++ b/awx/ui/client/lib/components/layout/side-nav-item.directive.js @@ -11,14 +11,14 @@ function AtSideNavItemController ($state, $scope, strings) { if ($scope.name === 'portal mode') { vm.isRoute = (current && current.indexOf('portalMode') === 0); } else if (current && current.indexOf($scope.route) === 0) { - if (current.indexOf('jobs.schedules') === 0 && $scope.route === 'jobs') { - vm.isRoute = false; - } else { - vm.isRoute = true; - } - } else { + if (current.indexOf('jobs.schedules') === 0 && $scope.route === 'jobs') { vm.isRoute = false; + } else { + vm.isRoute = true; } + } else { + vm.isRoute = false; + } }); vm.go = () => { diff --git a/awx/ui/client/lib/models/Me.js b/awx/ui/client/lib/models/Me.js index a606db9731..c2ee95d46e 100644 --- a/awx/ui/client/lib/models/Me.js +++ b/awx/ui/client/lib/models/Me.js @@ -12,7 +12,7 @@ function MeModel (method, resource, graft) { this.unset('results'); } - return this; + return this; }); } diff --git a/awx/ui/client/src/vendor.js b/awx/ui/client/src/vendor.js index bfa5e130d8..4af531a78f 100644 --- a/awx/ui/client/src/vendor.js +++ b/awx/ui/client/src/vendor.js @@ -8,6 +8,7 @@ require('~modules/codemirror/theme/elegant.css'); require('~modules/codemirror/addon/lint/lint.css'); require('~modules/nvd3/build/nv.d3.css'); require('~modules/ng-toast/dist/ngToast.min.css'); + require('jquery'); require('jquery-resize'); require('jquery-ui'); diff --git a/awx/ui/package.json b/awx/ui/package.json index 95f518a3e9..687b9d4ea0 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -58,7 +58,6 @@ "html-loader": "^0.5.1", "html-webpack-harddisk-plugin": "^0.1.0", "html-webpack-plugin": "^2.30.1", - "imports-loader": "^0.7.1", "jasmine-core": "^2.5.2", "jshint": "^2.9.4", "jshint-loader": "^0.8.3", @@ -80,7 +79,6 @@ "less-plugin-autoprefix": "^1.4.2", "load-grunt-configs": "^1.0.0", "load-grunt-tasks": "^3.5.0", - "minimist": "^1.2.0", "nightwatch": "^0.9.16", "ngtemplate-loader": "^2.0.1", "phantomjs-prebuilt": "^2.1.12", @@ -106,6 +104,7 @@ "angular-scheduler": "git+https://git@github.com/ansible/angular-scheduler#0.1.1", "angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#0.3.13", "angular-ui-router": "1.0.0-beta.3", + "babel-polyfill": "^6.26.0", "bootstrap": "^3.3.7", "bootstrap-datepicker": "^1.7.1", "codemirror": "^5.17.0",