diff options
Diffstat (limited to 'config')
-rw-r--r-- | config/application.rb | 1 | ||||
-rw-r--r-- | config/initializers/8_metrics.rb | 9 | ||||
-rw-r--r-- | config/initializers/console_message.rb | 10 | ||||
-rw-r--r-- | config/initializers/forbid_sidekiq_in_transactions.rb | 2 | ||||
-rw-r--r-- | config/initializers/session_store.rb | 26 | ||||
-rw-r--r-- | config/initializers/static_files.rb | 2 | ||||
-rw-r--r-- | config/initializers/trusted_proxies.rb | 13 | ||||
-rw-r--r-- | config/initializers/warden.rb | 16 | ||||
-rw-r--r-- | config/karma.config.js | 92 | ||||
-rw-r--r-- | config/prometheus/additional_metrics.yml | 12 | ||||
-rw-r--r-- | config/routes/group.rb | 7 | ||||
-rw-r--r-- | config/routes/profile.rb | 1 | ||||
-rw-r--r-- | config/routes/project.rb | 9 | ||||
-rw-r--r-- | config/routes/repository.rb | 1 | ||||
-rw-r--r-- | config/routes/user.rb | 7 | ||||
-rw-r--r-- | config/sidekiq_queues.yml | 3 | ||||
-rw-r--r-- | config/webpack.config.js | 184 |
17 files changed, 241 insertions, 154 deletions
diff --git a/config/application.rb b/config/application.rb index ad7338763f7..09f706e3d70 100644 --- a/config/application.rb +++ b/config/application.rb @@ -115,6 +115,7 @@ module Gitlab config.assets.precompile << "test.css" config.assets.precompile << "snippets.css" config.assets.precompile << "locale/**/app.js" + config.assets.precompile << "emoji_sprites.css" # Import gitlab-svgs directly from vendored directory config.assets.paths << "#{config.root}/node_modules/@gitlab-org/gitlab-svgs/dist" diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index 7cdf49159b4..8a851b89c56 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -119,7 +119,14 @@ def instrument_classes(instrumentation) end # rubocop:enable Metrics/AbcSize -if Gitlab::Metrics.enabled? +# With prometheus enabled by default this breaks all specs +# that stubs methods using `any_instance_of` for the models reloaded here. +# +# We should deprecate the usage of `any_instance_of` in the future +# check: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class +# +# Related issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/33587 +if Gitlab::Metrics.enabled? && !Rails.env.test? require 'pathname' require 'influxdb' require 'connection_pool' diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb new file mode 100644 index 00000000000..536ab337d85 --- /dev/null +++ b/config/initializers/console_message.rb @@ -0,0 +1,10 @@ +# rubocop:disable Rails/Output +if defined?(Rails::Console) + # note that this will not print out when using `spring` + justify = 15 + puts "-------------------------------------------------------------------------------------" + puts " Gitlab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab::REVISION})" + puts " Gitlab Shell:".ljust(justify) + Gitlab::Shell.new.version + puts " #{Gitlab::Database.adapter_name}:".ljust(justify) + Gitlab::Database.version + puts "-------------------------------------------------------------------------------------" +end diff --git a/config/initializers/forbid_sidekiq_in_transactions.rb b/config/initializers/forbid_sidekiq_in_transactions.rb index 4603123665d..deb94d7dbce 100644 --- a/config/initializers/forbid_sidekiq_in_transactions.rb +++ b/config/initializers/forbid_sidekiq_in_transactions.rb @@ -27,7 +27,7 @@ module Sidekiq Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead. MSG rescue Sidekiq::Worker::EnqueueFromTransactionError => e - Rails.logger.error(e.message) if Rails.env.production? + ::Rails.logger.error(e.message) if ::Rails.env.production? Gitlab::Sentry.track_exception(e) end end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index f2fde1e0048..da24881885e 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -15,19 +15,15 @@ cookie_key = if Rails.env.development? "_gitlab_session" end -if Rails.env.test? - Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" -else - sessions_config = Gitlab::Redis::SharedState.params - sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE +sessions_config = Gitlab::Redis::SharedState.params +sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE - Gitlab::Application.config.session_store( - :redis_store, # Using the cookie_store would enable session replay attacks. - servers: sessions_config, - key: cookie_key, - secure: Gitlab.config.gitlab.https, - httponly: true, - expires_in: Settings.gitlab['session_expire_delay'] * 60, - path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root - ) -end +Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + servers: sessions_config, + key: cookie_key, + secure: Gitlab.config.gitlab.https, + httponly: true, + expires_in: Settings.gitlab['session_expire_delay'] * 60, + path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root +) diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index d3a7a2b9f8b..6c28686e69a 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -34,7 +34,7 @@ if app.config.serve_static_files ) app.config.middleware.insert_before( Gitlab::Middleware::Static, - Gitlab::Middleware::WebpackProxy, + Gitlab::Webpack::DevServerMiddleware, proxy_path: app.config.webpack.public_path, proxy_host: dev_server.host, proxy_port: dev_server.port diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb index 0c32528311e..ca2eed664ed 100644 --- a/config/initializers/trusted_proxies.rb +++ b/config/initializers/trusted_proxies.rb @@ -22,3 +22,16 @@ end.compact Rails.application.config.action_dispatch.trusted_proxies = ( ['127.0.0.1', '::1'] + gitlab_trusted_proxies) + +# A monkey patch to make trusted proxies work with Rails 5.0. +# Inspired by https://github.com/rails/rails/issues/5223#issuecomment-263778719 +# Remove this monkey patch when upstream is fixed. +if Gitlab.rails5? + module TrustedProxyMonkeyPatch + def ip + @ip ||= (get_header("action_dispatch.remote_ip") || super).to_s + end + end + + ActionDispatch::Request.send(:include, TrustedProxyMonkeyPatch) +end diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb index ee034d21eae..8cc36820d3c 100644 --- a/config/initializers/warden.rb +++ b/config/initializers/warden.rb @@ -1,9 +1,21 @@ Rails.application.configure do |config| - Warden::Manager.after_set_user do |user, auth, opts| + Warden::Manager.after_set_user(scope: :user) do |user, auth, opts| Gitlab::Auth::UniqueIpsLimiter.limit_user!(user) end - Warden::Manager.before_failure do |env, opts| + Warden::Manager.before_failure(scope: :user) do |env, opts| Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env) end + + Warden::Manager.after_authentication(scope: :user) do |user, auth, opts| + ActiveSession.cleanup(user) + end + + Warden::Manager.after_set_user(scope: :user, only: :fetch) do |user, auth, opts| + ActiveSession.set(user, auth.request) + end + + Warden::Manager.before_logout(scope: :user) do |user, auth, opts| + ActiveSession.destroy(user || auth.user, auth.request.session.id) + end end diff --git a/config/karma.config.js b/config/karma.config.js index 691cda98861..28a688797d9 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -1,47 +1,87 @@ -var path = require('path'); -var webpack = require('webpack'); -var argumentsParser = require('commander'); -var webpackConfig = require('./webpack.config.js'); -var ROOT_PATH = path.resolve(__dirname, '..'); - -// remove problematic plugins -if (webpackConfig.plugins) { - webpackConfig.plugins = webpackConfig.plugins.filter(function(plugin) { - return !( - plugin instanceof webpack.optimize.CommonsChunkPlugin || - plugin instanceof webpack.optimize.ModuleConcatenationPlugin || - plugin instanceof webpack.DefinePlugin - ); - }); +const path = require('path'); +const glob = require('glob'); +const chalk = require('chalk'); +const webpack = require('webpack'); +const argumentsParser = require('commander'); +const webpackConfig = require('./webpack.config.js'); + +const ROOT_PATH = path.resolve(__dirname, '..'); + +function fatalError(message) { + console.error(chalk.red(`\nError: ${message}\n`)); + process.exit(1); } -var testFiles = argumentsParser +// disable problematic options +webpackConfig.entry = undefined; +webpackConfig.mode = 'development'; +webpackConfig.optimization.runtimeChunk = false; +webpackConfig.optimization.splitChunks = false; + +// use quicker sourcemap option +webpackConfig.devtool = 'cheap-inline-source-map'; + +const specFilters = argumentsParser .option( '-f, --filter-spec [filter]', 'Filter run spec files by path. Multiple filters are like a logical OR.', - (val, memo) => { - memo.push(val); + (filter, memo) => { + memo.push(filter, filter.replace(/\/?$/, '/**/*.js')); return memo; }, [] ) .parse(process.argv).filterSpec; -webpackConfig.plugins.push( - new webpack.DefinePlugin({ - 'process.env.TEST_FILES': JSON.stringify(testFiles), - }) -); +if (specFilters.length) { + const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/; + + // resolve filters + let filteredSpecFiles = specFilters.map(filter => + glob + .sync(filter, { + root: ROOT_PATH, + matchBase: true, + }) + .filter(path => path.endsWith('spec.js')) + ); + + // flatten + filteredSpecFiles = Array.prototype.concat.apply([], filteredSpecFiles); + + // remove duplicates + filteredSpecFiles = [...new Set(filteredSpecFiles)]; -webpackConfig.devtool = process.env.BABEL_ENV !== 'coverage' && 'cheap-inline-source-map'; + if (filteredSpecFiles.length < 1) { + fatalError('Your filter did not match any test files.'); + } + + if (!filteredSpecFiles.every(file => specsPath.test(file))) { + fatalError('Test files must be located within /spec/javascripts.'); + } + + const newContext = filteredSpecFiles.reduce((context, file) => { + const relativePath = file.replace(specsPath, ''); + context[file] = `./${relativePath}`; + return context; + }, {}); + + webpackConfig.plugins.push( + new webpack.ContextReplacementPlugin( + /spec[\\\/]javascripts$/, + path.join(ROOT_PATH, 'spec/javascripts'), + newContext + ) + ); +} // Karma configuration module.exports = function(config) { process.env.TZ = 'Etc/UTC'; - var progressReporter = process.env.CI ? 'mocha' : 'progress'; + const progressReporter = process.env.CI ? 'mocha' : 'progress'; - var karmaConfig = { + const karmaConfig = { basePath: ROOT_PATH, browsers: ['ChromeHeadlessCustom'], customLaunchers: { diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml index 10ca612b246..13732384953 100644 --- a/config/prometheus/additional_metrics.yml +++ b/config/prometheus/additional_metrics.yml @@ -103,10 +103,10 @@ - title: "Throughput" y_label: "Requests / Sec" required_metrics: - - nginx_responses_total + - nginx_server_requests weight: 1 queries: - - query_range: 'sum(rate(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (status_code)' + - query_range: 'sum(rate(nginx_server_requests{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) by (code)' unit: req / sec label: Status Code series: @@ -121,19 +121,19 @@ - title: "Latency" y_label: "Latency (ms)" required_metrics: - - nginx_upstream_response_msecs_avg + - nginx_server_requestMsec weight: 1 queries: - - query_range: 'avg(nginx_upstream_response_msecs_avg{%{environment_filter}})' + - query_range: 'avg(nginx_server_requestMsec{%{environment_filter}})' label: Upstream unit: ms - title: "HTTP Error Rate" y_label: "HTTP 500 Errors / Sec" required_metrics: - - nginx_responses_total + - nginx_server_requests weight: 1 queries: - - query_range: 'sum(rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m]))' + - query_range: 'sum(rate(nginx_server_requests{code="5xx", %{environment_filter}}[2m]))' label: HTTP Errors unit: "errors / sec" - group: System metrics (Kubernetes) diff --git a/config/routes/group.rb b/config/routes/group.rb index 170508e893d..7c4c3d370e0 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -58,6 +58,13 @@ constraints(::Constraints::GroupUrlConstrainer.new) do # On CE only index and show actions are needed resources :boards, only: [:index, :show] + + resources :runners, only: [:index, :edit, :update, :destroy, :show] do + member do + post :resume + post :pause + end + end end scope(path: '*id', diff --git a/config/routes/profile.rb b/config/routes/profile.rb index bcfc17a5f66..a9ba5ac2c0b 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -30,6 +30,7 @@ resource :profile, only: [:show, :update] do put :revoke end end + resources :active_sessions, only: [:index, :destroy] resources :emails, only: [:index, :create, :destroy] do member do put :resend_confirmation_instructions diff --git a/config/routes/project.rb b/config/routes/project.rb index 2a1bcb8cde2..5a1be1a8b73 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -161,7 +161,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end get :diff_for_path - get :update_branches get :branch_from get :branch_to end @@ -175,6 +174,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + resource :mirror, only: [:show, :update] do + member do + post :update_now + end + end + resources :pipelines, only: [:index, :new, :create, :show] do collection do resource :pipelines_settings, path: 'settings', only: [:show, :update] @@ -183,6 +188,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do member do get :stage + get :stage_ajax post :cancel post :retry get :builds @@ -410,6 +416,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do collection do post :toggle_shared_runners + post :toggle_group_runners end end diff --git a/config/routes/repository.rb b/config/routes/repository.rb index 9e506a1a43a..e2bf8d6a7ff 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -18,6 +18,7 @@ scope format: false do resources :compare, only: [:index, :create] do collection do get :diff_for_path + get :signatures end end diff --git a/config/routes/user.rb b/config/routes/user.rb index f8677693fab..bc7df5e7584 100644 --- a/config/routes/user.rb +++ b/config/routes/user.rb @@ -27,6 +27,13 @@ devise_scope :user do get '/users/almost_there' => 'confirmations#almost_there' end +scope '-/users', module: :users do + resources :terms, only: [:index] do + post :accept, on: :member + post :decline, on: :member + end +end + scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) do scope(path: 'users/:username', as: :user, diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 47fbbed44cf..e1e8f36b663 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -73,3 +73,6 @@ - [object_storage, 1] - [plugin, 1] - [pipeline_background, 1] + - [repository_update_remote_mirror, 1] + - [repository_remove_remote, 1] + diff --git a/config/webpack.config.js b/config/webpack.config.js index 39e9fbbd530..5096f35e808 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -1,4 +1,3 @@ -const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const glob = require('glob'); @@ -6,9 +5,7 @@ const webpack = require('webpack'); const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin; const CopyWebpackPlugin = require('copy-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); -const NameAllModulesPlugin = require('name-all-modules-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const ROOT_PATH = path.resolve(__dirname, '..'); const IS_PRODUCTION = process.env.NODE_ENV === 'production'; @@ -21,10 +18,12 @@ const NO_COMPRESSION = process.env.NO_COMPRESSION; let autoEntriesCount = 0; let watchAutoEntries = []; +const defaultEntries = ['./main']; function generateEntries() { // generate automatic entry points const autoEntries = {}; + const autoEntriesMap = {}; const pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'app/assets/javascripts'), }); @@ -33,25 +32,38 @@ function generateEntries() { function generateAutoEntries(path, prefix = '.') { const chunkPath = path.replace(/\/index\.js$/, ''); const chunkName = chunkPath.replace(/\//g, '.'); - autoEntries[chunkName] = `${prefix}/${path}`; + autoEntriesMap[chunkName] = `${prefix}/${path}`; } pageEntries.forEach(path => generateAutoEntries(path)); - autoEntriesCount = Object.keys(autoEntries).length; + const autoEntryKeys = Object.keys(autoEntriesMap); + autoEntriesCount = autoEntryKeys.length; + + // import ancestor entrypoints within their children + autoEntryKeys.forEach(entry => { + const entryPaths = [autoEntriesMap[entry]]; + const segments = entry.split('.'); + while (segments.pop()) { + const ancestor = segments.join('.'); + if (autoEntryKeys.includes(ancestor)) { + entryPaths.unshift(autoEntriesMap[ancestor]); + } + } + autoEntries[entry] = defaultEntries.concat(entryPaths); + }); const manualEntries = { - common: './commons/index.js', - main: './main.js', + default: defaultEntries, raven: './raven/index.js', - webpack_runtime: './webpack.js', - ide: './ide/index.js', }; return Object.assign(manualEntries, autoEntries); } const config = { + mode: IS_PRODUCTION ? 'production' : 'development', + context: path.join(ROOT_PATH, 'app/assets/javascripts'), entry: generateEntries, @@ -59,8 +71,36 @@ const config = { output: { path: path.join(ROOT_PATH, 'public/assets/webpack'), publicPath: '/assets/webpack/', - filename: IS_PRODUCTION ? '[name].[chunkhash].bundle.js' : '[name].bundle.js', - chunkFilename: IS_PRODUCTION ? '[name].[chunkhash].chunk.js' : '[name].chunk.js', + filename: IS_PRODUCTION ? '[name].[chunkhash:8].bundle.js' : '[name].bundle.js', + chunkFilename: IS_PRODUCTION ? '[name].[chunkhash:8].chunk.js' : '[name].chunk.js', + globalObject: 'this', // allow HMR and web workers to play nice + }, + + optimization: { + nodeEnv: false, + runtimeChunk: 'single', + splitChunks: { + maxInitialRequests: 4, + cacheGroups: { + default: false, + common: () => ({ + priority: 20, + name: 'main', + chunks: 'initial', + minChunks: autoEntriesCount * 0.9, + }), + vendors: { + priority: 10, + chunks: 'async', + test: /[\\/](node_modules|vendor[\\/]assets[\\/]javascripts)[\\/]/, + }, + commons: { + chunks: 'all', + minChunks: 2, + reuseExistingChunk: true, + }, + }, + }, }, module: { @@ -69,6 +109,9 @@ const config = { test: /\.js$/, exclude: /(node_modules|vendor\/assets)/, loader: 'babel-loader', + options: { + cacheDirectory: path.join(ROOT_PATH, 'tmp/cache/babel-loader'), + }, }, { test: /\.vue$/, @@ -89,10 +132,10 @@ const config = { { loader: 'worker-loader', options: { - inline: true, + name: '[name].[hash:8].worker.js', }, }, - { loader: 'babel-loader' }, + 'babel-loader', ], }, { @@ -100,7 +143,7 @@ const config = { exclude: /node_modules/, loader: 'file-loader', options: { - name: '[name].[hash].[ext]', + name: '[name].[hash:8].[ext]', }, }, { @@ -111,7 +154,7 @@ const config = { { loader: 'css-loader', options: { - name: '[name].[hash].[ext]', + name: '[name].[hash:8].[ext]', }, }, ], @@ -121,7 +164,7 @@ const config = { include: /node_modules\/katex\/dist\/fonts/, loader: 'file-loader', options: { - name: '[name].[hash].[ext]', + name: '[name].[hash:8].[ext]', }, }, { @@ -163,54 +206,6 @@ const config = { jQuery: 'jquery', }), - // assign deterministic module ids - new webpack.NamedModulesPlugin(), - new NameAllModulesPlugin(), - - // assign deterministic chunk ids - new webpack.NamedChunksPlugin(chunk => { - if (chunk.name) { - return chunk.name; - } - - const moduleNames = []; - - function collectModuleNames(m) { - // handle ConcatenatedModule which does not have resource nor context set - if (m.modules) { - m.modules.forEach(collectModuleNames); - return; - } - - const pagesBase = path.join(ROOT_PATH, 'app/assets/javascripts/pages'); - - if (m.resource.indexOf(pagesBase) === 0) { - moduleNames.push( - path - .relative(pagesBase, m.resource) - .replace(/\/index\.[a-z]+$/, '') - .replace(/\//g, '__') - ); - } else { - moduleNames.push(path.relative(m.context, m.resource)); - } - } - - chunk.forEachModule(collectModuleNames); - - const hash = crypto - .createHash('sha256') - .update(moduleNames.join('_')) - .digest('hex'); - - return `${moduleNames[0]}-${hash.substr(0, 6)}`; - }), - - // create cacheable common library bundles - new webpack.optimize.CommonsChunkPlugin({ - names: ['main', 'common', 'webpack_runtime'], - }), - // copy pre-compiled vendor libraries verbatim new CopyWebpackPlugin([ { @@ -257,20 +252,6 @@ const config = { if (IS_PRODUCTION) { config.devtool = 'source-map'; - config.plugins.push( - new webpack.NoEmitOnErrorsPlugin(), - new webpack.LoaderOptionsPlugin({ - minimize: true, - debug: false, - }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.optimize.UglifyJsPlugin({ - sourceMap: true, - }), - new webpack.DefinePlugin({ - 'process.env': { NODE_ENV: JSON.stringify('production') }, - }) - ); // compression can require a lot of compute time and is disabled in CI if (!NO_COMPRESSION) { @@ -289,29 +270,30 @@ if (IS_DEV_SERVER) { hot: DEV_SERVER_LIVERELOAD, inline: DEV_SERVER_LIVERELOAD, }; - config.plugins.push( - // watch node_modules for changes if we encounter a missing module compile error - new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules')), - - // watch for changes to our automatic entry point modules - { - apply(compiler) { - compiler.plugin('emit', (compilation, callback) => { - compilation.contextDependencies = [ - ...compilation.contextDependencies, - ...watchAutoEntries, - ]; - - // report our auto-generated bundle count - console.log( - `${autoEntriesCount} entries from '/pages' automatically added to webpack output.` - ); - - callback(); - }); - }, - } - ); + config.plugins.push({ + apply(compiler) { + compiler.hooks.emit.tapAsync('WatchForChangesPlugin', (compilation, callback) => { + const missingDeps = Array.from(compilation.missingDependencies); + const nodeModulesPath = path.join(ROOT_PATH, 'node_modules'); + const hasMissingNodeModules = missingDeps.some( + file => file.indexOf(nodeModulesPath) !== -1 + ); + + // watch for changes to missing node_modules + if (hasMissingNodeModules) compilation.contextDependencies.add(nodeModulesPath); + + // watch for changes to automatic entrypoints + watchAutoEntries.forEach(watchPath => compilation.contextDependencies.add(watchPath)); + + // report our auto-generated bundle count + console.log( + `${autoEntriesCount} entries from '/pages' automatically added to webpack output.` + ); + + callback(); + }); + }, + }); if (DEV_SERVER_LIVERELOAD) { config.plugins.push(new webpack.HotModuleReplacementPlugin()); } |