webpacker 2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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