From 159fdfe7ad1059985eca0109f46251f13da868de Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 21 Sep 2017 15:52:19 -0400 Subject: [PATCH 1/2] Fix how global dependencies are loaded in the UI The UI has a handful of dependencies attached to window (angular, jquery, lodash, etc). In the case of schedules, jquery was included and extended as expected, but then clobbered by another module. This prevented the Select2 dependency from working as expected. Rather than relying on webpack to export particular dependencies as global, that work is done in the vendor entry point now instead. --- awx/ui/build/webpack.base.js | 30 +++++++---------------------- awx/ui/build/webpack.development.js | 2 +- awx/ui/build/webpack.production.js | 8 +------- awx/ui/client/src/vendor.js | 16 +++++++++++++-- awx/ui/package.json | 2 -- 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/awx/ui/build/webpack.base.js b/awx/ui/build/webpack.base.js index dbdec3eafb..6a8c4ed781 100644 --- a/awx/ui/build/webpack.base.js +++ b/awx/ui/build/webpack.base.js @@ -49,7 +49,7 @@ const base = { chunks: false, excludeAssets: name => { const chunkNames = `(${CHUNKS.join('|')})`; - const outputPattern = new RegExp(`${chunkNames}\.[a-f0-9]+\.(js|css)$`, 'i'); + const outputPattern = new RegExp(`${chunkNames}\.[a-f0-9]+\.(js|css)(|\.map)$`, 'i'); return !outputPattern.test(name); } @@ -87,17 +87,6 @@ const base = { use: ['css-loader', 'less-loader'] }) }, - { - test: require.resolve('jquery'), - loader: 'expose-loader?$!expose-loader?jQuery!expose-loader?jquery' - }, - { - loader: 'script-loader', - test: [ - /node_modules\/javascript-detect-element-resize\/jquery.resize\.js$/, - /node_modules\/d3\/d3\.min.js$/ - ] - }, { test: /\.html$/, use: ['ngtemplate-loader', 'html-loader'], @@ -117,8 +106,7 @@ const base = { new webpack.ProvidePlugin({ jsyaml: 'js-yaml', CodeMirror: 'codemirror', - jsonlint: 'codemirror.jsonlint', - _: 'lodash' + jsonlint: 'codemirror.jsonlint' }), new ExtractTextPlugin('css/[name].[hash].css'), new CleanWebpackPlugin([STATIC_PATH, COVERAGE_PATH, LANGUAGES_PATH], { @@ -182,13 +170,7 @@ const base = { filename: INDEX_OUTPUT, inject: false, chunks: CHUNKS, - chunksSortMode: (chunk) => { - if (chunk.names[0] === 'polyfill' || chunk.names[0] === 'vendor') { - return -1; - } - - return 1; - } + chunksSortMode: chunk => chunk.names[0] === 'vendor' ? -1 : 1 }) ], resolve: { @@ -200,12 +182,14 @@ const base = { '~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': '~modules/jquery/dist/jquery.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-tz-extensions$': '~modules/angular-tz-extensions/lib/angular-tz-extensions.js', 'angular-ui-router$': '~modules/angular-ui-router/release/angular-ui-router.js', 'angular-ui-router-state-events$': '~modules/angular-ui-router/release/stateEvents.js', 'ng-toast-provider$': '~modules/ng-toast/src/scripts/provider.js', diff --git a/awx/ui/build/webpack.development.js b/awx/ui/build/webpack.development.js index 2d85a4bd7a..5e936c0a7e 100644 --- a/awx/ui/build/webpack.development.js +++ b/awx/ui/build/webpack.development.js @@ -5,7 +5,7 @@ const _ = require('lodash'); const base = require('./webpack.base'); const development = { - devtool: 'cheap-source-map' + devtool: 'source-map' }; module.exports = _.merge(base, development); diff --git a/awx/ui/build/webpack.production.js b/awx/ui/build/webpack.production.js index e709962765..feaa6d13f7 100644 --- a/awx/ui/build/webpack.production.js +++ b/awx/ui/build/webpack.production.js @@ -25,13 +25,7 @@ const production = { filename: INSTALL_RUNNING_OUTPUT, inject: false, chunks: CHUNKS, - chunksSortMode: (chunk) => { - if (chunk.names[0] === 'polyfill' || chunk.names[0] === 'vendor') { - return -1; - } - - return 1; - } + chunksSortMode: chunk => chunk.names[0] === 'vendor' ? -1 : 1 }) ] }; diff --git a/awx/ui/client/src/vendor.js b/awx/ui/client/src/vendor.js index 4af531a78f..11c361d314 100644 --- a/awx/ui/client/src/vendor.js +++ b/awx/ui/client/src/vendor.js @@ -1,3 +1,4 @@ +// Theme require('~assets/custom-theme/jquery-ui-1.10.3.custom.min.css'); require('~assets/ansible-bootstrap.min.css'); require('~assets/fontcustom/fontcustom.css'); @@ -9,17 +10,28 @@ require('~modules/codemirror/addon/lint/lint.css'); require('~modules/nvd3/build/nv.d3.css'); require('~modules/ng-toast/dist/ngToast.min.css'); -require('jquery'); +// jQuery + extensions +global.jQuery = require('jquery'); +global.jquery = global.jQuery; +global.$ = global.jQuery; require('jquery-resize'); require('jquery-ui'); require('bootstrap'); require('bootstrap-datepicker'); -require('moment'); require('select2'); + +// Standalone libs +global._ = require('lodash'); +require('moment'); +require('rrule'); require('sprintf-js'); require('reconnectingwebsocket'); + +// D3 + extensions require('d3'); require('nvd3'); + +// Angular require('angular'); require('angular-cookies'); require('angular-sanitize'); diff --git a/awx/ui/package.json b/awx/ui/package.json index f79ca4aa32..a5f520ece7 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -47,7 +47,6 @@ "eslint-import-resolver-webpack": "^0.8.3", "eslint-loader": "^1.9.0", "eslint-plugin-import": "^2.7.0", - "expose-loader": "^0.7.3", "extract-text-webpack-plugin": "^3.0.0", "grunt": "^1.0.1", "grunt-angular-gettext": "^2.2.3", @@ -60,7 +59,6 @@ "html-webpack-plugin": "^2.30.1", "jasmine-core": "^2.5.2", "jshint": "^2.9.4", - "jshint-loader": "^0.8.3", "jshint-stylish": "^2.2.0", "json-loader": "^0.5.4", "karma": "^1.4.1", From 34ca4e56237a697e8ded3c2cb1962f5c7cdc5eef Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 22 Sep 2017 10:51:39 -0400 Subject: [PATCH 2/2] Make asset hashes unique per file instead of per build --- awx/ui/build/webpack.base.js | 4 ++-- awx/ui/build/webpack.production.js | 5 +++++ awx/ui/build/webpack.watch.js | 25 +++++++++++++++++++++++-- awx/ui/package.json | 4 ++-- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/awx/ui/build/webpack.base.js b/awx/ui/build/webpack.base.js index 6a8c4ed781..e0b6998974 100644 --- a/awx/ui/build/webpack.base.js +++ b/awx/ui/build/webpack.base.js @@ -27,7 +27,7 @@ const VENDOR_ENTRY = path.join(SOURCE_PATH, 'vendor.js'); const INDEX_ENTRY = path.join(CLIENT_PATH, 'index.template.ejs'); const INDEX_OUTPUT = path.join(UI_PATH, 'templates/ui/index.html'); const THEME_ENTRY = path.join(LIB_PATH, 'theme', 'index.less'); -const OUTPUT = 'js/[name].[hash].js'; +const OUTPUT = 'js/[name].[chunkhash].js'; const CHUNKS = ['vendor', 'app']; const VENDOR = VENDOR_ENTRY; @@ -108,7 +108,7 @@ const base = { CodeMirror: 'codemirror', jsonlint: 'codemirror.jsonlint' }), - new ExtractTextPlugin('css/[name].[hash].css'), + new ExtractTextPlugin('css/[name].[chunkhash].css'), new CleanWebpackPlugin([STATIC_PATH, COVERAGE_PATH, LANGUAGES_PATH], { root: UI_PATH, verbose: false diff --git a/awx/ui/build/webpack.production.js b/awx/ui/build/webpack.production.js index feaa6d13f7..4c61eef4a4 100644 --- a/awx/ui/build/webpack.production.js +++ b/awx/ui/build/webpack.production.js @@ -26,6 +26,11 @@ const production = { inject: false, chunks: CHUNKS, chunksSortMode: chunk => chunk.names[0] === 'vendor' ? -1 : 1 + }), + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production') + } }) ] }; diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index 85431adddc..1c2f9e5270 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -3,14 +3,21 @@ const path = require('path'); const _ = require('lodash'); const webpack = require('webpack'); const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin'); +const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); 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}`; +const OUTPUT = 'js/[name].js'; const development = require('./webpack.development'); const watch = { + cache: true, + devtool: 'cheap-source-map', + output: { + filename: OUTPUT + }, module: { rules: [ { @@ -22,10 +29,24 @@ const watch = { ] }, plugins: [ + new HardSourceWebpackPlugin({ + cacheDirectory: 'node_modules/.cache/hard-source/[confighash]', + recordsPath: 'node_modules/.cache/hard-source/[confighash]/records.json', + configHash: config => { + return require('node-object-hash')({ sort: false }).hash(config); + }, + environmentHash: { + root: process.cwd(), + directories: ['node_modules'], + files: ['package.json'] + } + }), new HtmlWebpackHarddiskPlugin(), new webpack.HotModuleReplacementPlugin() ], devServer: { + hot: true, + inline: true, contentBase: path.resolve(__dirname, '..', 'static'), stats: 'minimal', publicPath: '/static/', @@ -35,7 +56,8 @@ const watch = { '/': { target: TARGET, secure: false, - ws: false + ws: false, + bypass: req => req.originalUrl.includes('hot-update.json') }, '/websocket': { target: TARGET, @@ -50,4 +72,3 @@ 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/package.json b/awx/ui/package.json index a5f520ece7..505a27810b 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -54,6 +54,7 @@ "grunt-concurrent": "^2.3.0", "grunt-contrib-jshint": "^1.0.0", "grunt-newer": "^1.2.0", + "hard-source-webpack-plugin": "^0.4.9", "html-loader": "^0.5.1", "html-webpack-harddisk-plugin": "^0.1.0", "html-webpack-plugin": "^2.30.1", @@ -79,9 +80,8 @@ "load-grunt-tasks": "^3.5.0", "ngtemplate-loader": "^2.0.1", "nightwatch": "^0.9.16", + "node-object-hash": "^1.3.0", "phantomjs-prebuilt": "^2.1.12", - "script-loader": "^0.7.0", - "style-loader": "^0.18.2", "time-grunt": "^1.4.0", "uglifyjs-webpack-plugin": "^0.4.6", "webpack": "^3.0.0",