Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix asset host support & improve output path #397

Merged
merged 8 commits into from
May 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.bundle
/pkg
/test/test_app/log
node_modules
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 <app> --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.

Expand Down
28 changes: 14 additions & 14 deletions lib/install/bin/webpack-dev-server.tt
Original file line number Diff line number Diff line change
Expand Up @@ -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
23 changes: 10 additions & 13 deletions lib/install/bin/webpack.tt
Original file line number Diff line number Diff line change
Expand Up @@ -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
25 changes: 18 additions & 7 deletions lib/install/config/webpack/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we set ASSET_HOST to dev server host in development when it's enabled? That way we don't need to replace manifest plugin.

if (env.NODE_ENV === "development") {
  env.ASSET_HOST = `//${host}:${port}` // relative to origin
}

OR

if (env.NODE_ENV === "development") {
  env.ASSET_HOST = `${devServer.https ? 'https': 'http'}://${devServer.host}:${devServer.port}`
}

We would be able to do this in development config without if, but guess we can't since we have much shared logic here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I described the reason for not doing it this way in the PR:

Fixes that webpacker:compile and bin/webpack would generate dev server URLs in development mode. This should only happen when using bin/webpack-dev-server.

That said, I cleaned things up in 2a94cdf by constructing and setting ASSET_HOST in bin/webpack-dev-server.

}

module.exports = {
devServer,
env,
paths,
loadersDir,
publicUrl,
publicPath
output
}
9 changes: 4 additions & 5 deletions lib/install/config/webpack/development.server.js
Original file line number Diff line number Diff line change
@@ -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
}
})
14 changes: 7 additions & 7 deletions lib/install/config/webpack/paths.yml
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome 👍 been thinking to do this

manifest: manifest.json # ~/public/:output/:manifest
config: config/webpack # ~/:config
node_modules: node_modules # ~/:node_modules
extensions:
- .coffee
- .js
Expand Down
12 changes: 8 additions & 4 deletions lib/install/config/webpack/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -26,8 +26,8 @@ module.exports = {

output: {
filename: '[name].js',
path: resolve(paths.output, paths.entry),
publicPath
path: output.path,
publicPath: output.publicPath
},

module: {
Expand All @@ -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: {
Expand Down
6 changes: 3 additions & 3 deletions lib/tasks/webpacker/clobber.rake
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions lib/tasks/webpacker/compile.rake
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -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
Expand Down
50 changes: 31 additions & 19 deletions lib/webpacker/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍰 🎉

@default_paths ||= HashWithIndifferentAccess.new(YAML.load(default_file_path.read)["default"])
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/webpacker/manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 1 addition & 6 deletions test/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down