webpacker 2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +21 -21
  4. data/CHANGELOG.md +107 -4
  5. data/Gemfile +3 -1
  6. data/Gemfile.lock +15 -8
  7. data/README.md +137 -937
  8. data/docs/assets.md +106 -0
  9. data/docs/css.md +82 -0
  10. data/docs/deployment.md +39 -0
  11. data/docs/env.md +62 -0
  12. data/docs/es6.md +53 -0
  13. data/docs/folder-structure.md +66 -0
  14. data/docs/misc.md +23 -0
  15. data/docs/props.md +105 -0
  16. data/docs/testing.md +45 -0
  17. data/docs/troubleshooting.md +65 -0
  18. data/docs/typescript.md +115 -0
  19. data/docs/webpack-dev-server.md +32 -0
  20. data/docs/webpack.md +108 -0
  21. data/docs/yarn.md +12 -0
  22. data/lib/install/angular.rb +4 -7
  23. data/lib/install/bin/webpack-dev-server.tt +35 -11
  24. data/lib/install/bin/webpack.tt +3 -4
  25. data/lib/install/config/.babelrc +1 -0
  26. data/lib/install/config/.postcssrc.yml +1 -2
  27. data/lib/install/config/webpack/development.js +2 -31
  28. data/lib/install/config/webpack/environment.js +3 -0
  29. data/lib/install/config/webpack/production.js +2 -34
  30. data/lib/install/config/webpack/test.js +2 -5
  31. data/lib/install/config/webpacker.yml +20 -2
  32. data/lib/install/elm.rb +6 -11
  33. data/lib/install/examples/vue/hello_vue.js +31 -2
  34. data/lib/install/react.rb +2 -5
  35. data/lib/install/template.rb +3 -8
  36. data/lib/install/vue.rb +4 -7
  37. data/lib/tasks/webpacker.rake +1 -1
  38. data/lib/tasks/webpacker/{check_webpack_binstubs.rake → check_binstubs.rake} +3 -2
  39. data/lib/tasks/webpacker/check_node.rake +8 -6
  40. data/lib/tasks/webpacker/check_yarn.rake +2 -2
  41. data/lib/tasks/webpacker/clobber.rake +2 -3
  42. data/lib/tasks/webpacker/compile.rake +16 -18
  43. data/lib/tasks/webpacker/verify_install.rake +5 -5
  44. data/lib/tasks/webpacker/yarn_install.rake +1 -1
  45. data/lib/webpacker.rb +15 -11
  46. data/lib/webpacker/commands.rb +22 -0
  47. data/lib/webpacker/compiler.rb +66 -10
  48. data/lib/webpacker/configuration.rb +54 -38
  49. data/lib/webpacker/dev_server.rb +47 -0
  50. data/lib/webpacker/dev_server_proxy.rb +24 -0
  51. data/lib/webpacker/helper.rb +23 -5
  52. data/lib/webpacker/instance.rb +44 -0
  53. data/lib/webpacker/manifest.rb +58 -34
  54. data/lib/webpacker/railtie.rb +22 -3
  55. data/lib/webpacker/version.rb +2 -1
  56. data/package.json +37 -7
  57. data/package/asset_host.js +21 -0
  58. data/package/config.js +8 -0
  59. data/package/environment.js +95 -0
  60. data/package/environments/development.js +47 -0
  61. data/package/environments/production.js +34 -0
  62. data/package/environments/test.js +3 -0
  63. data/package/index.js +16 -0
  64. data/package/loaders/babel.js +11 -0
  65. data/{lib/install/config/loaders/core → package/loaders}/coffee.js +0 -0
  66. data/{lib/install/config/loaders/installers → package/loaders}/elm.js +4 -5
  67. data/{lib/install/config/loaders/core → package/loaders}/erb.js +0 -0
  68. data/package/loaders/file.js +15 -0
  69. data/package/loaders/style.js +31 -0
  70. data/{lib/install/config/loaders/installers/angular.js → package/loaders/typescript.js} +1 -1
  71. data/package/loaders/vue.js +12 -0
  72. data/test/compiler_test.rb +20 -0
  73. data/test/configuration_test.rb +43 -19
  74. data/test/dev_server_test.rb +24 -0
  75. data/test/helper_test.rb +21 -5
  76. data/test/manifest_test.rb +25 -19
  77. data/test/test_app/public/packs/manifest.json +3 -1
  78. data/test/webpacker_test_helper.rb +40 -0
  79. data/webpacker.gemspec +1 -1
  80. data/yarn.lock +4701 -578
  81. metadata +52 -29
  82. data/lib/install/config/loaders/core/assets.js +0 -12
  83. data/lib/install/config/loaders/core/babel.js +0 -5
  84. data/lib/install/config/loaders/core/sass.js +0 -15
  85. data/lib/install/config/loaders/installers/react.js +0 -5
  86. data/lib/install/config/loaders/installers/vue.js +0 -13
  87. data/lib/install/config/webpack/configuration.js +0 -35
  88. data/lib/install/config/webpack/shared.js +0 -58
  89. data/lib/webpacker/env.rb +0 -23
  90. data/lib/webpacker/file_loader.rb +0 -24
  91. data/test/env_test.rb +0 -14
  92. data/test/webpacker_test.rb +0 -15
@@ -1,5 +1,5 @@
1
1
  namespace :webpacker do
2
- desc "Support for older Rails versions.Install all JavaScript dependencies as specified via Yarn"
2
+ desc "Support for older Rails versions. Install all JavaScript dependencies as specified via Yarn"
3
3
  task :yarn_install, [:arg1, :arg2] do |task, args|
4
4
  system "yarn #{args[:arg1]} #{args[:arg2]}"
5
5
  end
data/lib/webpacker.rb CHANGED
@@ -1,24 +1,28 @@
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+ require "active_support/logger"
3
+ require "active_support/tagged_logging"
4
+
1
5
  module Webpacker
2
6
  extend self
3
7
 
4
- def bootstrap
5
- Webpacker::Env.load
6
- Webpacker::Configuration.load
7
- Webpacker::Manifest.load
8
+ def instance=(instance)
9
+ @instance = instance
8
10
  end
9
11
 
10
- def compile
11
- Webpacker::Compiler.compile
12
- Webpacker::Manifest.load
12
+ def instance
13
+ @instance ||= Webpacker::Instance.new
13
14
  end
14
15
 
15
- def env
16
- Webpacker::Env.current.inquiry
17
- end
16
+ delegate :logger, :logger=, :env, to: :instance
17
+ delegate :config, :compiler, :manifest, :commands, :dev_server, to: :instance
18
+ delegate :bootstrap, :clobber, :compile, to: :commands
18
19
  end
19
20
 
20
- require "webpacker/env"
21
+ require "webpacker/instance"
21
22
  require "webpacker/configuration"
22
23
  require "webpacker/manifest"
23
24
  require "webpacker/compiler"
25
+ require "webpacker/commands"
26
+ require "webpacker/dev_server"
27
+
24
28
  require "webpacker/railtie" if defined?(Rails)
@@ -0,0 +1,22 @@
1
+ class Webpacker::Commands
2
+ delegate :config, :compiler, :manifest, to: :@webpacker
3
+
4
+ def initialize(webpacker)
5
+ @webpacker = webpacker
6
+ end
7
+
8
+ def clobber
9
+ config.public_output_path.rmtree if config.public_output_path.exist?
10
+ config.cache_path.rmtree if config.cache_path.exist?
11
+ end
12
+
13
+ def bootstrap
14
+ config.refresh
15
+ manifest.refresh
16
+ end
17
+
18
+ def compile
19
+ compiler.compile
20
+ manifest.refresh
21
+ end
22
+ end
@@ -1,20 +1,76 @@
1
- require "rake"
1
+ require "open3"
2
+ require "digest/sha1"
2
3
 
3
- module Webpacker::Compiler
4
- extend self
4
+ class Webpacker::Compiler
5
+ # Additional paths that test compiler needs to watch
6
+ # Webpacker::Compiler.watched_paths << 'bower_components'
7
+ mattr_accessor(:watched_paths) { [] }
8
+
9
+ # Additional environment variables that the compiler is being run with
10
+ # Webpacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key'
11
+ mattr_accessor(:env) { {} }
12
+
13
+ delegate :config, :logger, to: :@webpacker
14
+
15
+ def initialize(webpacker)
16
+ @webpacker = webpacker
17
+ end
5
18
 
6
19
  def compile
7
- compile_task.invoke
8
- compile_task.reenable
20
+ if stale?
21
+ record_compilation_digest
22
+ run_webpack
23
+ end
24
+ end
25
+
26
+ # Returns true if all the compiled packs are up to date with the underlying asset files.
27
+ def fresh?
28
+ watched_files_digest == last_compilation_digest
29
+ end
30
+
31
+ # Returns true if the compiled packs are out of date with the underlying asset files.
32
+ def stale?
33
+ !fresh?
9
34
  end
10
35
 
11
36
  private
12
- def compile_task
13
- @compile_task ||= load_rake_task("webpacker:compile")
37
+ def last_compilation_digest
38
+ compilation_digest_path.read if compilation_digest_path.exist? && config.public_manifest_path.exist?
39
+ end
40
+
41
+ def watched_files_digest
42
+ files = Dir[*default_watched_paths, *watched_paths].reject { |f| File.directory?(f) }
43
+ Digest::SHA1.hexdigest(files.map { |f| "#{File.basename(f)}/#{File.mtime(f).utc.to_i}" }.join("/"))
44
+ end
45
+
46
+ def record_compilation_digest
47
+ config.cache_path.mkpath
48
+ compilation_digest_path.write(watched_files_digest)
49
+ end
50
+
51
+ def run_webpack
52
+ logger.info "Compiling…"
53
+
54
+ sterr, stdout, status = Open3.capture3(webpack_env, "#{RbConfig.ruby} ./bin/webpack")
55
+
56
+ if status.success?
57
+ logger.info "Compiled all packs in #{config.public_output_path}"
58
+ else
59
+ logger.error "Compilation failed:\n#{sterr}\n#{stdout}"
60
+ end
61
+
62
+ status.success?
63
+ end
64
+
65
+ def default_watched_paths
66
+ ["#{config.source_path}/**/*", "yarn.lock", "package.json", "config/webpack/**/*"].freeze
67
+ end
68
+
69
+ def compilation_digest_path
70
+ config.cache_path.join(".last-compilation-digest")
14
71
  end
15
72
 
16
- def load_rake_task(name)
17
- @load_rakefile ||= Rake.load_rakefile(Rails.root.join("Rakefile"))
18
- Rake::Task[name]
73
+ def webpack_env
74
+ env.merge("NODE_ENV" => @webpacker.env, "ASSET_HOST" => ActionController::Base.helpers.compute_asset_host)
19
75
  end
20
76
  end
@@ -1,59 +1,75 @@
1
- # Loads webpacker configuration from config/webpacker.yml
1
+ class Webpacker::Configuration
2
+ delegate :root_path, :config_path, :env, to: :@webpacker
2
3
 
3
- require "webpacker/file_loader"
4
+ def initialize(webpacker)
5
+ @webpacker = webpacker
6
+ end
4
7
 
5
- class Webpacker::Configuration < Webpacker::FileLoader
6
- class << self
7
- def entry_path
8
- source_path.join(fetch(:source_entry_path))
9
- end
8
+ def refresh
9
+ @data = load
10
+ end
10
11
 
11
- def output_path
12
- public_path.join(fetch(:public_output_path))
13
- end
12
+ def dev_server
13
+ fetch(:dev_server)
14
+ end
14
15
 
15
- def manifest_path
16
- output_path.join("manifest.json")
17
- end
16
+ def compile?
17
+ fetch(:compile)
18
+ end
18
19
 
19
- def source_path
20
- Rails.root.join(source)
21
- end
20
+ def source_path
21
+ root_path.join(fetch(:source_path))
22
+ end
22
23
 
23
- def public_path
24
- Rails.root.join("public")
25
- end
24
+ def source_entry_path
25
+ source_path.join(fetch(:source_entry_path))
26
+ end
26
27
 
27
- def file_path(root: Rails.root)
28
- root.join("config/webpacker.yml")
29
- end
28
+ def public_path
29
+ root_path.join("public")
30
+ end
30
31
 
31
- def default_file_path
32
- file_path(root: Pathname.new(__dir__).join("../install"))
33
- end
32
+ def public_output_path
33
+ public_path.join(fetch(:public_output_path))
34
+ end
34
35
 
35
- def source
36
- fetch(:source_path)
37
- end
36
+ def public_manifest_path
37
+ public_output_path.join("manifest.json")
38
+ end
39
+
40
+ def cache_manifest?
41
+ fetch(:cache_manifest)
42
+ end
38
43
 
44
+ def cache_path
45
+ root_path.join(fetch(:cache_path))
46
+ end
47
+
48
+ private
39
49
  def fetch(key)
40
50
  data.fetch(key, defaults[key])
41
51
  end
42
52
 
43
53
  def data
44
- load if Webpacker.env.development?
45
- raise Webpacker::FileLoader::FileLoaderError.new("Webpacker::Configuration.load must be called first") unless instance
46
- instance.data
54
+ @data ||= load
47
55
  end
48
56
 
49
- def defaults
50
- @defaults ||= HashWithIndifferentAccess.new(YAML.load(default_file_path.read)["default"])
57
+ def load
58
+ YAML.load(config_path.read)[env].deep_symbolize_keys
59
+
60
+ rescue Errno::ENOENT => e
61
+ raise "Webpacker configuration file not found #{config_path}. " \
62
+ "Please run rails webpacker:install " \
63
+ "Error: #{e.message}"
64
+
65
+ rescue Psych::SyntaxError => e
66
+ raise "YAML syntax error occurred while parsing #{config_path}. " \
67
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
68
+ "Error: #{e.message}"
51
69
  end
52
- end
53
70
 
54
- private
55
- def load
56
- return super unless File.exist?(@path)
57
- HashWithIndifferentAccess.new(YAML.load(File.read(@path))[Webpacker.env])
71
+ def defaults
72
+ @defaults ||= \
73
+ HashWithIndifferentAccess.new(YAML.load_file(File.expand_path("../../install/config/webpacker.yml", __FILE__))[env])
58
74
  end
59
75
  end
@@ -0,0 +1,47 @@
1
+ class Webpacker::DevServer
2
+ delegate :config, to: :@webpacker
3
+
4
+ def initialize(webpacker)
5
+ @webpacker = webpacker
6
+ end
7
+
8
+ def running?
9
+ Socket.tcp(host, port, connect_timeout: 1).close
10
+ true
11
+ rescue Errno::ECONNREFUSED, NoMethodError
12
+ false
13
+ end
14
+
15
+ def hot_module_replacing?
16
+ fetch(:hmr)
17
+ end
18
+
19
+ def host
20
+ fetch(:host)
21
+ end
22
+
23
+ def port
24
+ fetch(:port)
25
+ end
26
+
27
+ def https?
28
+ fetch(:https)
29
+ end
30
+
31
+ def protocol
32
+ https? ? "https" : "http"
33
+ end
34
+
35
+ def host_with_port
36
+ "#{host}:#{port}"
37
+ end
38
+
39
+ private
40
+ def fetch(key)
41
+ config.dev_server.fetch(key, defaults[key])
42
+ end
43
+
44
+ def defaults
45
+ config.send(:defaults)[:dev_server]
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ require "rack/proxy"
2
+
3
+ class Webpacker::DevServerProxy < Rack::Proxy
4
+ def rewrite_response(response)
5
+ status, headers, body = response
6
+ headers.delete "transfer-encoding"
7
+ headers.delete "content-length" if Webpacker.dev_server.running? && Webpacker.dev_server.https?
8
+ response
9
+ end
10
+
11
+ def perform_request(env)
12
+ if env["PATH_INFO"] =~ /#{public_output_uri_path}/ && Webpacker.dev_server.running?
13
+ env["HTTP_HOST"] = Webpacker.dev_server.host_with_port
14
+ super(env)
15
+ else
16
+ @app.call(env)
17
+ end
18
+ end
19
+
20
+ private
21
+ def public_output_uri_path
22
+ Webpacker.config.public_output_path.relative_path_from(Webpacker.config.public_path)
23
+ end
24
+ end
@@ -9,7 +9,7 @@ module Webpacker::Helper
9
9
  # In production mode:
10
10
  # <%= asset_pack_path 'calendar.css' %> # => "/packs/calendar-1016838bab065ae1e122.css"
11
11
  def asset_pack_path(name, **options)
12
- asset_path(Webpacker::Manifest.lookup(name), **options)
12
+ asset_path(Webpacker.manifest.lookup(name), **options)
13
13
  end
14
14
  # Creates a script tag that references the named pack file, as compiled by Webpack per the entries list
15
15
  # in config/webpack/shared.js. By default, this list is auto-generated to match everything in
@@ -24,24 +24,42 @@ module Webpacker::Helper
24
24
  # # In production mode:
25
25
  # <%= javascript_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
26
26
  # <script src="/packs/calendar-1016838bab065ae1e314.js" data-turbolinks-track="reload"></script>
27
- def javascript_pack_tag(name, **options)
28
- javascript_include_tag(Webpacker::Manifest.lookup("#{name}#{compute_asset_extname(name, type: :javascript)}"), **options)
27
+ def javascript_pack_tag(*names, **options)
28
+ javascript_include_tag(*sources_from_pack_manifest(names, type: :javascript), **options)
29
29
  end
30
30
 
31
31
  # Creates a link tag that references the named pack file, as compiled by Webpack per the entries list
32
32
  # in config/webpack/shared.js. By default, this list is auto-generated to match everything in
33
33
  # app/javascript/packs/*.js. In production mode, the digested reference is automatically looked up.
34
34
  #
35
+ # Note: If the development server is running and hot module replacement is active, this will return nothing.
36
+ # In that setup you need to configure your styles to be inlined in your JavaScript for hot reloading.
37
+ #
35
38
  # Examples:
36
39
  #
37
40
  # # In development mode:
38
41
  # <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
39
42
  # <link rel="stylesheet" media="screen" href="/packs/calendar.css" data-turbolinks-track="reload" />
40
43
  #
44
+ # # In development mode with hot module replacement:
45
+ # <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
46
+ # nil
47
+ #
41
48
  # # In production mode:
42
49
  # <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
43
50
  # <link rel="stylesheet" media="screen" href="/packs/calendar-1016838bab065ae1e122.css" data-turbolinks-track="reload" />
44
- def stylesheet_pack_tag(name, **options)
45
- stylesheet_link_tag(Webpacker::Manifest.lookup("#{name}#{compute_asset_extname(name, type: :stylesheet)}"), **options)
51
+ def stylesheet_pack_tag(*names, **options)
52
+ unless Webpacker.dev_server.running? && Webpacker.dev_server.hot_module_replacing?
53
+ stylesheet_link_tag(*sources_from_pack_manifest(names, type: :stylesheet), **options)
54
+ end
46
55
  end
56
+
57
+ private
58
+ def sources_from_pack_manifest(names, type:)
59
+ names.map { |name| Webpacker.manifest.lookup(pack_name_with_extension(name, type: type)) }
60
+ end
61
+
62
+ def pack_name_with_extension(name, type:)
63
+ "#{name}#{compute_asset_extname(name, type: type)}"
64
+ end
47
65
  end
@@ -0,0 +1,44 @@
1
+ class Webpacker::Instance
2
+ cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) }
3
+
4
+ attr_reader :root_path, :config_path
5
+
6
+ def initialize(root_path: Rails.root, config_path: Rails.root.join("config/webpacker.yml"))
7
+ @root_path, @config_path = root_path, config_path
8
+ end
9
+
10
+ def env
11
+ (ENV["NODE_ENV"].presence_in(available_environments) ||
12
+ Rails.env.presence_in(available_environments) ||
13
+ "production".freeze).inquiry
14
+ end
15
+
16
+ def config
17
+ @config ||= Webpacker::Configuration.new self
18
+ end
19
+
20
+ def compiler
21
+ @compiler ||= Webpacker::Compiler.new self
22
+ end
23
+
24
+ def dev_server
25
+ @dev_server ||= Webpacker::DevServer.new self
26
+ end
27
+
28
+ def manifest
29
+ @manifest ||= Webpacker::Manifest.new self
30
+ end
31
+
32
+ def commands
33
+ @commands ||= Webpacker::Commands.new self
34
+ end
35
+
36
+ private
37
+ def available_environments
38
+ if config_path.exist?
39
+ YAML.load(config_path.read).keys
40
+ else
41
+ [].freeze
42
+ end
43
+ end
44
+ end