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 dbdec3eafb73..6a8c4ed78155 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 2d85a4bd7a7b..5e936c0a7e4e 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 e709962765fe..feaa6d13f755 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 4af531a78fa0..11c361d31463 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 f79ca4aa325a..a5f520ece7e2 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 6a8c4ed78155..e0b6998974f1 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 feaa6d13f755..4c61eef4a4a0 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 85431adddc8f..1c2f9e52700d 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 a5f520ece7e2..505a27810b0e 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",