diff --git a/.gitignore b/.gitignore index e9134faa0..207100846 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.bundle /pkg +/test/test_app/log node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index d43a2076f..27b1d5381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,30 @@ ### Fixed - Update `.babelrc` to fix compilation issues - [#306](https://github.com/rails/webpacker/issues/306) +- Duplicated asset hosts - [#320](https://github.com/rails/webpacker/issues/320), [#397](https://github.com/rails/webpacker/pull/397) + +- Missing asset host when defined as a `Proc` or on `ActionController::Base.asset_host` directly - [#397](https://github.com/rails/webpacker/pull/397) + +- Incorrect asset host when running `webpacker:compile` or `bin/webpack` in development mode - [#397](https://github.com/rails/webpacker/pull/397) + ### Added - [Elm](http://elm-lang.org) support. You can now add Elm support via the following methods: - New app: `rails new --webpack=elm` - Within an existing app: `rails webpacker:install:elm` +- Support for custom `output` paths independent of `entry` in `paths.yml`. `output` is also now relative to `public/`. - [#397](https://github.com/rails/webpacker/pull/397) + + Before (compile to `public/packs`): + ```yaml + entry: packs + output: public + ``` + After (compile to `public/sweet/js`): + ```yaml + entry: packs + output: sweet/js + ``` + ## [1.2] - 2017-04-27 Some of the changes made requires you to run below commands to install new changes. diff --git a/lib/install/bin/webpack-dev-server.tt b/lib/install/bin/webpack-dev-server.tt index 5ea4a6e1c..3a736fc96 100644 --- a/lib/install/bin/webpack-dev-server.tt +++ b/lib/install/bin/webpack-dev-server.tt @@ -10,26 +10,26 @@ RAILS_ENV = ENV["RAILS_ENV"] ENV["NODE_ENV"] ||= RAILS_ENV NODE_ENV = ENV["NODE_ENV"] -APP_PATH = File.expand_path("../", __dir__) -CONFIG_PATH = File.join(APP_PATH, "config/webpack/paths.yml") +APP_PATH = File.expand_path("../", __dir__) -begin - paths = YAML.load(File.read(CONFIG_PATH))[NODE_ENV] - - NODE_MODULES_PATH = File.join(APP_PATH.shellescape, paths["node_modules"]) - WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"]) - - DEV_SERVER_CONFIG = "#{WEBPACK_CONFIG_PATH}/development.server.js" +def load_yaml_config(config_file) + YAML.load_file(File.join(APP_PATH, config_file))[NODE_ENV] rescue Errno::ENOENT, NoMethodError - puts "Configuration not found in config/webpacker/paths.yml." + puts "Configuration not found in #{config_file}." puts "Please run bundle exec rails webpacker:install to install webpacker" exit! end -DEV_SERVER_BIN = "yarn run webpack-dev-server" -newenv = { "NODE_PATH" => NODE_MODULES_PATH } -cmdline = [DEV_SERVER_BIN, "--", "--progress", "--color", "--config", DEV_SERVER_CONFIG] + ARGV +paths = load_yaml_config("config/webpack/paths.yml") +NODE_MODULES_PATH = File.join(APP_PATH, paths["node_modules"]) +WEBPACK_CONFIG = File.join(APP_PATH, paths["config"], "development.server.js") + +dev_server = load_yaml_config("config/webpack/development.server.yml") +DEV_SERVER_HOST = "http#{"s" if dev_server["https"]}://#{dev_server["host"]}:#{dev_server["port"]}" + +newenv = { "NODE_PATH" => NODE_MODULES_PATH.shellescape, "ASSET_HOST" => DEV_SERVER_HOST.shellescape } +cmdline = ["yarn", "run", "webpack-dev-server", "--", "--progress", "--color", "--config", WEBPACK_CONFIG] + ARGV Dir.chdir(APP_PATH) do - exec newenv, cmdline.join(' ') + exec newenv, *cmdline end diff --git a/lib/install/bin/webpack.tt b/lib/install/bin/webpack.tt index 3f0480ad1..09371db1a 100644 --- a/lib/install/bin/webpack.tt +++ b/lib/install/bin/webpack.tt @@ -10,26 +10,23 @@ RAILS_ENV = ENV["RAILS_ENV"] ENV["NODE_ENV"] ||= RAILS_ENV NODE_ENV = ENV["NODE_ENV"] -APP_PATH = File.expand_path("../", __dir__) -CONFIG_PATH = File.join(APP_PATH, "config/webpack/paths.yml") +APP_PATH = File.expand_path("../", __dir__) -begin - paths = YAML.load(File.read(CONFIG_PATH))[NODE_ENV] - - NODE_MODULES_PATH = File.join(APP_PATH.shellescape, paths["node_modules"]) - WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"]) +def load_yaml_config(config_file) + YAML.load_file(File.join(APP_PATH, config_file))[NODE_ENV] rescue Errno::ENOENT, NoMethodError - puts "Configuration not found in config/webpack/paths.yml" + puts "Configuration not found in #{config_file}." puts "Please run bundle exec rails webpacker:install to install webpacker" exit! end -WEBPACK_BIN = "yarn run webpack" -WEBPACK_CONFIG = "#{WEBPACK_CONFIG_PATH}/#{NODE_ENV}.js" +paths = load_yaml_config("config/webpack/paths.yml") +NODE_MODULES_PATH = File.join(APP_PATH, paths["node_modules"]) +WEBPACK_CONFIG = File.join(APP_PATH, paths["config"], "#{NODE_ENV}.js") -newenv = { "NODE_PATH" => NODE_MODULES_PATH } -cmdline = [WEBPACK_BIN, "--", "--config", WEBPACK_CONFIG] + ARGV +newenv = { "NODE_PATH" => NODE_MODULES_PATH.shellescape } +cmdline = ["yarn", "run", "webpack", "--", "--config", WEBPACK_CONFIG] + ARGV Dir.chdir(APP_PATH) do - exec newenv, cmdline.join(' ') + exec newenv, *cmdline end diff --git a/lib/install/config/webpack/configuration.js b/lib/install/config/webpack/configuration.js index 904af8229..4f55505a8 100644 --- a/lib/install/config/webpack/configuration.js +++ b/lib/install/config/webpack/configuration.js @@ -10,17 +10,28 @@ const loadersDir = join(__dirname, 'loaders') const paths = safeLoad(readFileSync(join(configPath, 'paths.yml'), 'utf8'))[env.NODE_ENV] const devServer = safeLoad(readFileSync(join(configPath, 'development.server.yml'), 'utf8'))[env.NODE_ENV] -// Compute public path based on environment and ASSET_HOST in production -const ifHasCDN = env.ASSET_HOST !== undefined && env.NODE_ENV === 'production' -const devServerUrl = `http://${devServer.host}:${devServer.port}/${paths.entry}/` -const publicUrl = ifHasCDN ? `${env.ASSET_HOST}/${paths.entry}/` : `/${paths.entry}/` -const publicPath = env.NODE_ENV !== 'production' && devServer.enabled ? devServerUrl : publicUrl +function removeOuterSlashes(string) { + return string.replace(/^\/*/, '').replace(/\/*$/, '') +} + +function formatPublicPath(host = '', path = '') { + let formattedHost = removeOuterSlashes(host) + if (formattedHost && !/^http/i.test(formattedHost)) { + formattedHost = `//${formattedHost}` + } + const formattedPath = removeOuterSlashes(path) + return `${formattedHost}/${formattedPath}/` +} + +const output = { + path: resolve('public', paths.output), + publicPath: formatPublicPath(env.ASSET_HOST, paths.output) +} module.exports = { devServer, env, paths, loadersDir, - publicUrl, - publicPath + output } diff --git a/lib/install/config/webpack/development.server.js b/lib/install/config/webpack/development.server.js index f8c7c12cf..08dfff1e4 100644 --- a/lib/install/config/webpack/development.server.js +++ b/lib/install/config/webpack/development.server.js @@ -1,18 +1,17 @@ // Note: You must restart bin/webpack-dev-server for changes to take effect -const { resolve } = require('path') const merge = require('webpack-merge') const devConfig = require('./development.js') -const { devServer, publicPath, paths } = require('./configuration.js') +const { devServer, output } = require('./configuration.js') module.exports = merge(devConfig, { devServer: { host: devServer.host, port: devServer.port, + contentBase: output.path, + publicPath: output.publicPath, compress: true, headers: { 'Access-Control-Allow-Origin': '*' }, - historyApiFallback: true, - contentBase: resolve(paths.output, paths.entry), - publicPath + historyApiFallback: true } }) diff --git a/lib/install/config/webpack/paths.yml b/lib/install/config/webpack/paths.yml index 26ab8facc..5c3550ccf 100644 --- a/lib/install/config/webpack/paths.yml +++ b/lib/install/config/webpack/paths.yml @@ -1,12 +1,12 @@ # Note: You must restart bin/webpack-dev-server for changes to take effect -default: &default - config: config/webpack - entry: packs - output: public - manifest: manifest.json - node_modules: node_modules - source: app/javascript +default: &default # ~ = Rails.root + source: app/javascript # ~/:source + entry: packs # ~/:source/:entry + output: packs # ~/public/:output + manifest: manifest.json # ~/public/:output/:manifest + config: config/webpack # ~/:config + node_modules: node_modules # ~/:node_modules extensions: - .coffee - .js diff --git a/lib/install/config/webpack/shared.js b/lib/install/config/webpack/shared.js index 035e1e008..53a0b5895 100644 --- a/lib/install/config/webpack/shared.js +++ b/lib/install/config/webpack/shared.js @@ -9,7 +9,7 @@ const { sync } = require('glob') const ExtractTextPlugin = require('extract-text-webpack-plugin') const ManifestPlugin = require('webpack-manifest-plugin') const extname = require('path-complete-extname') -const { env, paths, publicPath, loadersDir } = require('./configuration.js') +const { env, paths, output, loadersDir } = require('./configuration.js') const extensionGlob = `**/*{${paths.extensions.join(',')}}*` const packPaths = sync(join(paths.source, paths.entry, extensionGlob)) @@ -26,8 +26,8 @@ module.exports = { output: { filename: '[name].js', - path: resolve(paths.output, paths.entry), - publicPath + path: output.path, + publicPath: output.publicPath }, module: { @@ -37,7 +37,11 @@ module.exports = { plugins: [ new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))), new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'), - new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }) + new ManifestPlugin({ + fileName: paths.manifest, + publicPath: output.publicPath, + writeToFileEmit: true + }) ], resolve: { diff --git a/lib/tasks/webpacker/clobber.rake b/lib/tasks/webpacker/clobber.rake index c8c88028c..5d1d6fe8d 100644 --- a/lib/tasks/webpacker/clobber.rake +++ b/lib/tasks/webpacker/clobber.rake @@ -3,9 +3,9 @@ require "webpacker/configuration" namespace :webpacker do desc "Remove the webpack compiled output directory" task clobber: ["webpacker:verify_install", :environment] do - packs_path = Webpacker::Configuration.packs_path - FileUtils.rm_r(packs_path) if File.exist?(packs_path) - puts "Removed webpack output path directory #{packs_path}" + output_path = Webpacker::Configuration.output_path + FileUtils.rm_r(output_path) if File.exist?(output_path) + puts "Removed webpack output path directory #{output_path}" end end diff --git a/lib/tasks/webpacker/compile.rake b/lib/tasks/webpacker/compile.rake index ac02ba900..1ed5632d6 100644 --- a/lib/tasks/webpacker/compile.rake +++ b/lib/tasks/webpacker/compile.rake @@ -6,7 +6,7 @@ namespace :webpacker do desc "Compile javascript packs using webpack for production with digests" task compile: ["webpacker:verify_install", :environment] do puts "Compiling webpacker assets 🎉" - asset_host = Rails.application.config.action_controller.asset_host + asset_host = ActionController::Base.helpers.compute_asset_host asset_env = asset_host ? "ASSET_HOST=#{asset_host}" : "" result = `#{asset_env} NODE_ENV=#{Webpacker.env} ./bin/webpack --json` @@ -15,7 +15,7 @@ namespace :webpacker do exit! $?.exitstatus end - puts "Compiled digests for all packs in #{Webpacker::Configuration.packs_path}: " + puts "Compiled digests for all packs in #{Webpacker::Configuration.entry_path}: " puts JSON.parse(File.read(Webpacker::Configuration.manifest_path)) end end diff --git a/lib/webpacker/configuration.rb b/lib/webpacker/configuration.rb index e50ff82da..ad330ef2f 100644 --- a/lib/webpacker/configuration.rb +++ b/lib/webpacker/configuration.rb @@ -4,42 +4,54 @@ class Webpacker::Configuration < Webpacker::FileLoader class << self - def config_path - Rails.root.join(paths.fetch(:config, "config/webpack")) - end - def entry_path - Rails.root.join(source_path, paths.fetch(:entry, "packs")) + source_path.join(fetch(:entry)) end - def file_path - Rails.root.join("config", "webpack", "paths.yml") + def output_path + public_path.join(fetch(:output)) end def manifest_path - Rails.root.join(packs_path, paths.fetch(:manifest, "manifest.json")) + output_path.join(fetch(:manifest)) end - def packs_path - Rails.root.join(output_path, paths.fetch(:entry, "packs")) + def source_path + Rails.root.join(source) end - def paths - load if Webpacker.env.development? - raise Webpacker::FileLoader::FileLoaderError.new("Webpacker::Configuration.load must be called first") unless instance - instance.data + def public_path + Rails.root.join("public") end - def output_path - Rails.root.join(paths.fetch(:output, "public")) + def config_path + Rails.root.join(fetch(:config)) + end + + def file_path(root: Rails.root) + root.join("config/webpack/paths.yml") + end + + def default_file_path + file_path(root: Pathname.new(__dir__).join("../install")) end def source - paths.fetch(:source, "app/javascript") + fetch(:source) end - def source_path - Rails.root.join(source) + def fetch(key) + paths.fetch(key, default_paths[key]) + end + + def paths + load if Webpacker.env.development? + raise Webpacker::FileLoader::FileLoaderError.new("Webpacker::Configuration.load must be called first") unless instance + instance.data + end + + def default_paths + @default_paths ||= HashWithIndifferentAccess.new(YAML.load(default_file_path.read)["default"]) end end diff --git a/lib/webpacker/manifest.rb b/lib/webpacker/manifest.rb index 0c0dc6dcc..671c1e5e2 100644 --- a/lib/webpacker/manifest.rb +++ b/lib/webpacker/manifest.rb @@ -24,7 +24,7 @@ def lookup(name) end def lookup_path(name) - Rails.root.join(File.join(Webpacker::Configuration.output_path, lookup(name))) + Rails.root.join(File.join(Webpacker::Configuration.public_path, lookup(name))) end private diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 1c491554a..f3bc51356 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -21,13 +21,8 @@ def test_manifest_path assert_equal Webpacker::Configuration.manifest_path.to_s, manifest_path end - def test_packs_path - packs_path = File.join(File.dirname(__FILE__), "test_app/public/packs").to_s - assert_equal Webpacker::Configuration.packs_path.to_s, packs_path - end - def test_output_path - output_path = File.join(File.dirname(__FILE__), "test_app/public").to_s + output_path = File.join(File.dirname(__FILE__), "test_app/public/packs").to_s assert_equal Webpacker::Configuration.output_path.to_s, output_path end