webpacker 4.0.7 → 4.1.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.node-version +1 -1
  3. data/.rubocop.yml +2 -1
  4. data/.travis.yml +7 -4
  5. data/CHANGELOG.md +212 -137
  6. data/Gemfile.lock +78 -59
  7. data/README.md +110 -3
  8. data/docs/css.md +12 -0
  9. data/docs/deployment.md +38 -9
  10. data/docs/docker.md +25 -6
  11. data/docs/engines.md +40 -3
  12. data/docs/es6.md +19 -1
  13. data/docs/troubleshooting.md +37 -9
  14. data/docs/typescript.md +8 -5
  15. data/docs/webpack-dev-server.md +1 -1
  16. data/docs/webpack.md +18 -3
  17. data/gemfiles/Gemfile-rails.6.0.x +9 -0
  18. data/lib/install/bin/webpack +0 -1
  19. data/lib/install/bin/webpack-dev-server +0 -1
  20. data/lib/install/coffee.rb +1 -1
  21. data/lib/install/config/babel.config.js +10 -10
  22. data/lib/install/config/webpacker.yml +2 -1
  23. data/lib/install/elm.rb +1 -1
  24. data/lib/install/erb.rb +2 -2
  25. data/lib/install/examples/angular/hello_angular/polyfills.ts +2 -2
  26. data/lib/install/examples/react/babel.config.js +16 -14
  27. data/lib/install/examples/svelte/app.svelte +11 -0
  28. data/lib/install/examples/svelte/hello_svelte.js +20 -0
  29. data/lib/install/loaders/elm.js +9 -6
  30. data/lib/install/loaders/svelte.js +9 -0
  31. data/lib/install/loaders/typescript.js +1 -1
  32. data/lib/install/svelte.rb +29 -0
  33. data/lib/install/typescript.rb +1 -1
  34. data/lib/install/vue.rb +1 -1
  35. data/lib/tasks/installers.rake +1 -0
  36. data/lib/tasks/webpacker.rake +2 -0
  37. data/lib/tasks/webpacker/clean.rake +15 -0
  38. data/lib/tasks/webpacker/compile.rake +1 -1
  39. data/lib/tasks/webpacker/yarn_install.rake +15 -0
  40. data/lib/webpacker.rb +1 -1
  41. data/lib/webpacker/commands.rb +26 -0
  42. data/lib/webpacker/compiler.rb +15 -8
  43. data/lib/webpacker/configuration.rb +9 -1
  44. data/lib/webpacker/dev_server.rb +1 -1
  45. data/lib/webpacker/dev_server_proxy.rb +2 -8
  46. data/lib/webpacker/helper.rb +39 -13
  47. data/lib/webpacker/railtie.rb +4 -0
  48. data/lib/webpacker/version.rb +1 -1
  49. data/package.json +36 -36
  50. data/package/__tests__/config.js +0 -23
  51. data/package/config.js +2 -10
  52. data/package/config_types/config_list.js +3 -3
  53. data/package/config_types/config_object.js +1 -1
  54. data/package/environments/base.js +2 -2
  55. data/package/environments/development.js +1 -1
  56. data/package/environments/production.js +12 -0
  57. data/package/rules/babel.js +1 -1
  58. data/package/rules/node_modules.js +2 -2
  59. data/package/rules/sass.js +1 -1
  60. data/package/utils/__tests__/get_style_rule.js +9 -0
  61. data/package/utils/deep_merge.js +5 -5
  62. data/package/utils/get_style_rule.js +7 -12
  63. data/package/utils/helpers.js +9 -9
  64. data/test/command_test.rb +6 -0
  65. data/test/compiler_test.rb +5 -6
  66. data/test/configuration_test.rb +36 -27
  67. data/test/dev_server_test.rb +22 -0
  68. data/test/helper_test.rb +34 -0
  69. data/test/rake_tasks_test.rb +6 -0
  70. data/test/test_app/bin/webpack +0 -1
  71. data/test/test_app/bin/webpack-dev-server +0 -1
  72. data/test/test_app/config/webpacker.yml +1 -0
  73. data/test/test_app/public/packs/manifest.json +3 -0
  74. data/webpacker.gemspec +1 -0
  75. data/yarn.lock +1934 -1634
  76. metadata +24 -4
@@ -38,8 +38,8 @@
38
38
  // import 'classlist.js'; // Run `npm install --save classlist.js`.
39
39
 
40
40
  /** Evergreen browsers require these. **/
41
- import 'core-js/es6/reflect';
42
- import 'core-js/es7/reflect';
41
+ import 'core-js/es/reflect';
42
+ import 'core-js/proposals/reflect-metadata';
43
43
 
44
44
 
45
45
  /**
@@ -18,15 +18,17 @@ module.exports = function(api) {
18
18
  return {
19
19
  presets: [
20
20
  isTestEnv && [
21
- require('@babel/preset-env').default,
21
+ '@babel/preset-env',
22
22
  {
23
23
  targets: {
24
24
  node: 'current'
25
- }
26
- }
25
+ },
26
+ modules: 'commonjs'
27
+ },
28
+ '@babel/preset-react'
27
29
  ],
28
30
  (isProductionEnv || isDevelopmentEnv) && [
29
- require('@babel/preset-env').default,
31
+ '@babel/preset-env',
30
32
  {
31
33
  forceAllTransforms: true,
32
34
  useBuiltIns: 'entry',
@@ -36,7 +38,7 @@ module.exports = function(api) {
36
38
  }
37
39
  ],
38
40
  [
39
- require('@babel/preset-react').default,
41
+ '@babel/preset-react',
40
42
  {
41
43
  development: isDevelopmentEnv || isTestEnv,
42
44
  useBuiltIns: true
@@ -44,24 +46,24 @@ module.exports = function(api) {
44
46
  ]
45
47
  ].filter(Boolean),
46
48
  plugins: [
47
- require('babel-plugin-macros'),
48
- require('@babel/plugin-syntax-dynamic-import').default,
49
- isTestEnv && require('babel-plugin-dynamic-import-node'),
50
- require('@babel/plugin-transform-destructuring').default,
49
+ 'babel-plugin-macros',
50
+ '@babel/plugin-syntax-dynamic-import',
51
+ isTestEnv && 'babel-plugin-dynamic-import-node',
52
+ '@babel/plugin-transform-destructuring',
51
53
  [
52
- require('@babel/plugin-proposal-class-properties').default,
54
+ '@babel/plugin-proposal-class-properties',
53
55
  {
54
56
  loose: true
55
57
  }
56
58
  ],
57
59
  [
58
- require('@babel/plugin-proposal-object-rest-spread').default,
60
+ '@babel/plugin-proposal-object-rest-spread',
59
61
  {
60
62
  useBuiltIns: true
61
63
  }
62
64
  ],
63
65
  [
64
- require('@babel/plugin-transform-runtime').default,
66
+ '@babel/plugin-transform-runtime',
65
67
  {
66
68
  helpers: false,
67
69
  regenerator: true,
@@ -69,13 +71,13 @@ module.exports = function(api) {
69
71
  }
70
72
  ],
71
73
  [
72
- require('@babel/plugin-transform-regenerator').default,
74
+ '@babel/plugin-transform-regenerator',
73
75
  {
74
76
  async: false
75
77
  }
76
78
  ],
77
79
  isProductionEnv && [
78
- require('babel-plugin-transform-react-remove-prop-types').default,
80
+ 'babel-plugin-transform-react-remove-prop-types',
79
81
  {
80
82
  removeImport: true
81
83
  }
@@ -0,0 +1,11 @@
1
+ <script>
2
+ export let name;
3
+ </script>
4
+
5
+ <style>
6
+ h1 {
7
+ color: #FF3E00;
8
+ }
9
+ </style>
10
+
11
+ <h1>Hello {name}!</h1>
@@ -0,0 +1,20 @@
1
+ /* eslint no-console: 0 */
2
+ // Run this example by adding <%= javascript_pack_tag 'hello_svelte' %> (and
3
+ // <%= stylesheet_pack_tag 'hello_svelte' %> if you have styles in your component)
4
+ // to the head of your layout file,
5
+ // like app/views/layouts/application.html.erb.
6
+ // All it does is render <div>Hello Svelte!</div> at the bottom of the page.
7
+
8
+ import App from '../app.svelte'
9
+
10
+ document.addEventListener('DOMContentLoaded', () => {
11
+ const app = new App({
12
+ target: document.body,
13
+ props: {
14
+ name: 'Svelte'
15
+ }
16
+ });
17
+
18
+ window.app = app;
19
+ })
20
+
@@ -1,18 +1,21 @@
1
1
  const { resolve } = require('path')
2
2
 
3
3
  const isProduction = process.env.NODE_ENV === 'production'
4
+ const isDevelopment = process.env.NODE_ENV === 'development'
4
5
  const elmSource = resolve(process.cwd())
5
6
  const elmBinary = `${elmSource}/node_modules/.bin/elm`
6
7
 
7
- const elmDefaultOptions = { cwd: elmSource, pathToElm: elmBinary }
8
- const developmentOptions = Object.assign({}, elmDefaultOptions, {
9
- verbose: true,
10
- debug: true
11
- })
8
+ const options = {
9
+ cwd: elmSource,
10
+ pathToElm: elmBinary,
11
+ optimize: isProduction,
12
+ verbose: isDevelopment,
13
+ debug: isDevelopment
14
+ }
12
15
 
13
16
  const elmWebpackLoader = {
14
17
  loader: 'elm-webpack-loader',
15
- options: isProduction ? elmDefaultOptions : developmentOptions
18
+ options: options
16
19
  }
17
20
 
18
21
  module.exports = {
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ test: /\.svelte$/,
3
+ use: [{
4
+ loader: 'svelte-loader',
5
+ options: {
6
+ hotReload: true
7
+ }
8
+ }],
9
+ }
@@ -1,7 +1,7 @@
1
1
  const PnpWebpackPlugin = require('pnp-webpack-plugin')
2
2
 
3
3
  module.exports = {
4
- test: /\.(ts|tsx)?(\.erb)?$/,
4
+ test: /\.tsx?(\.erb)?$/,
5
5
  use: [
6
6
  {
7
7
  loader: 'ts-loader',
@@ -0,0 +1,29 @@
1
+ require "webpacker/configuration"
2
+
3
+ say "Copying svelte loader to config/webpack/loaders"
4
+ copy_file "#{__dir__}/loaders/svelte.js", Rails.root.join("config/webpack/loaders/svelte.js").to_s
5
+
6
+ say "Adding svelte loader to config/webpack/environment.js"
7
+ insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
8
+ "const svelte = require('./loaders/svelte')\n",
9
+ after: /require\(('|")@rails\/webpacker\1\);?\n/
10
+
11
+ insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
12
+ "environment.loaders.prepend('svelte', svelte)\n",
13
+ before: "module.exports"
14
+
15
+ say "Copying Svelte example entry file to #{Webpacker.config.source_entry_path}"
16
+ copy_file "#{__dir__}/examples/svelte/hello_svelte.js",
17
+ "#{Webpacker.config.source_entry_path}/hello_svelte.js"
18
+
19
+ say "Copying Svelte app file to #{Webpacker.config.source_path}"
20
+ copy_file "#{__dir__}/examples/svelte/app.svelte",
21
+ "#{Webpacker.config.source_path}/app.svelte"
22
+
23
+ say "Installing all Svelte dependencies"
24
+ run "yarn add svelte svelte-loader"
25
+
26
+ say "Updating webpack paths to include .svelte file extension"
27
+ insert_into_file Webpacker.config.config_path, "- .svelte\n".indent(4), after: /\s+extensions:\n/
28
+
29
+ say "Webpacker now supports Svelte 🎉", :green
@@ -21,7 +21,7 @@ copy_file "#{__dir__}/loaders/typescript.js", Rails.root.join("config/webpack/lo
21
21
  say "Adding typescript loader to config/webpack/environment.js"
22
22
  insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
23
23
  "const typescript = require('./loaders/typescript')\n",
24
- after: "require('@rails/webpacker')\n"
24
+ after: /require\(('|")@rails\/webpacker\1\);?\n/
25
25
 
26
26
  insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
27
27
  "environment.loaders.prepend('typescript', typescript)\n",
@@ -6,7 +6,7 @@ copy_file "#{__dir__}/loaders/vue.js", Rails.root.join("config/webpack/loaders/v
6
6
  say "Adding vue loader plugin to config/webpack/environment.js"
7
7
  insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
8
8
  "const { VueLoaderPlugin } = require('vue-loader')\n",
9
- after: "require('@rails/webpacker')\n"
9
+ after: /require\(('|")@rails\/webpacker\1\);?\n/
10
10
 
11
11
  insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
12
12
  "environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())\n",
@@ -6,6 +6,7 @@ installers = {
6
6
  "Erb": :erb,
7
7
  "Coffee": :coffee,
8
8
  "Typescript": :typescript,
9
+ "Svelte": :svelte,
9
10
  "Stimulus": :stimulus
10
11
  }.freeze
11
12
 
@@ -2,6 +2,7 @@ tasks = {
2
2
  "webpacker:info" => "Provides information on Webpacker's environment",
3
3
  "webpacker:install" => "Installs and setup webpack with Yarn",
4
4
  "webpacker:compile" => "Compiles webpack bundles based on environment",
5
+ "webpacker:clean" => "Remove old compiled webpacks",
5
6
  "webpacker:clobber" => "Removes the webpack compiled output directory",
6
7
  "webpacker:check_node" => "Verifies if Node.js is installed",
7
8
  "webpacker:check_yarn" => "Verifies if Yarn is installed",
@@ -13,6 +14,7 @@ tasks = {
13
14
  "webpacker:install:vue" => "Installs and setup example Vue component",
14
15
  "webpacker:install:angular" => "Installs and setup example Angular component",
15
16
  "webpacker:install:elm" => "Installs and setup example Elm component",
17
+ "webpacker:install:svelte" => "Installs and setup example Svelte component",
16
18
  "webpacker:install:stimulus" => "Installs and setup example Stimulus component",
17
19
  "webpacker:install:erb" => "Installs Erb loader with an example",
18
20
  "webpacker:install:coffee" => "Installs CoffeeScript loader with an example",
@@ -0,0 +1,15 @@
1
+ require "webpacker/configuration"
2
+
3
+ namespace :webpacker do
4
+ desc "Remove old compiled webpacks"
5
+ task :clean, [:keep] => ["webpacker:verify_install", :environment] do |_, args|
6
+ Webpacker.clean(Integer(args.keep || 2))
7
+ end
8
+ end
9
+
10
+ # Run clean if the assets:clean is run
11
+ if Rake::Task.task_defined?("assets:clean")
12
+ Rake::Task["assets:clean"].enhance do
13
+ Rake::Task["webpacker:clean"].invoke
14
+ end
15
+ end
@@ -26,7 +26,7 @@ end
26
26
  namespace :webpacker do
27
27
  desc "Compile JavaScript packs using webpack for production with digests"
28
28
  task compile: ["webpacker:verify_install", :environment] do
29
- Webpacker.with_node_env("production") do
29
+ Webpacker.with_node_env(ENV.fetch("NODE_ENV", "production")) do
30
30
  ensure_log_goes_to_stdout do
31
31
  if Webpacker.compile
32
32
  # Successful compilation!
@@ -2,5 +2,20 @@ namespace :webpacker do
2
2
  desc "Support for older Rails versions. Install all JavaScript dependencies as specified via Yarn"
3
3
  task :yarn_install do
4
4
  system "yarn install --no-progress"
5
+
6
+ exit(1) unless $?.success?
5
7
  end
6
8
  end
9
+
10
+ def enhance_yarn_install
11
+ Rake::Task["yarn:install"].enhance do
12
+ exit(1) unless $?.success?
13
+ end
14
+ end
15
+
16
+ if Rake::Task.task_defined?("yarn:install")
17
+ enhance_yarn_install
18
+ else
19
+ # this is mainly for pre-5.x era
20
+ Rake::Task.define_task("yarn:install" => "webpacker:yarn_install")
21
+ end
@@ -24,7 +24,7 @@ module Webpacker
24
24
 
25
25
  delegate :logger, :logger=, :env, to: :instance
26
26
  delegate :config, :compiler, :manifest, :commands, :dev_server, to: :instance
27
- delegate :bootstrap, :clobber, :compile, to: :commands
27
+ delegate :bootstrap, :clean, :clobber, :compile, to: :commands
28
28
  end
29
29
 
30
30
  require "webpacker/instance"
@@ -5,6 +5,23 @@ class Webpacker::Commands
5
5
  @webpacker = webpacker
6
6
  end
7
7
 
8
+ def clean(count_to_keep = 2)
9
+ if config.public_output_path.exist? && config.public_manifest_path.exist?
10
+ files_in_manifest = process_manifest_hash(manifest.refresh)
11
+ files_to_be_removed = files_in_manifest.flat_map do |file_in_manifest|
12
+ file_prefix, file_ext = file_in_manifest.scan(/(.*)[0-9a-f]{20}(.*)/).first
13
+ versions_of_file = Dir.glob("#{file_prefix}*#{file_ext}").grep(/#{file_prefix}[0-9a-f]{20}#{file_ext}/)
14
+ versions_of_file.map do |version_of_file|
15
+ next if version_of_file == file_in_manifest
16
+
17
+ [version_of_file, File.mtime(version_of_file).utc.to_i]
18
+ end.compact.sort_by(&:last).reverse.drop(count_to_keep).map(&:first)
19
+ end
20
+
21
+ files_to_be_removed.each { |f| File.delete f }
22
+ end
23
+ end
24
+
8
25
  def clobber
9
26
  config.public_output_path.rmtree if config.public_output_path.exist?
10
27
  config.cache_path.rmtree if config.cache_path.exist?
@@ -19,4 +36,13 @@ class Webpacker::Commands
19
36
  manifest.refresh if success
20
37
  end
21
38
  end
39
+
40
+ private
41
+ def process_manifest_hash(manifest_hash)
42
+ manifest_hash.values.map do |value|
43
+ next process_manifest_hash(value) if value.is_a?(Hash)
44
+
45
+ File.join(config.root_path, "public", value)
46
+ end.flatten
47
+ end
22
48
  end
@@ -19,9 +19,15 @@ class Webpacker::Compiler
19
19
  def compile
20
20
  if stale?
21
21
  run_webpack.tap do |success|
22
- record_compilation_digest if success
22
+ # We used to only record the digest on success
23
+ # However, the output file is still written on error, (at least with ts-loader), meaning that the
24
+ # digest should still be updated. If it's not, you can end up in a situation where a recompile doesn't
25
+ # take place when it should.
26
+ # See https://github.com/rails/webpacker/issues/2113
27
+ record_compilation_digest
23
28
  end
24
29
  else
30
+ logger.info "Everything's up-to-date. Nothing to do"
25
31
  true
26
32
  end
27
33
  end
@@ -56,7 +62,7 @@ class Webpacker::Compiler
56
62
  end
57
63
 
58
64
  def run_webpack
59
- logger.info "Compiling"
65
+ logger.info "Compiling..."
60
66
 
61
67
  stdout, stderr, status = Open3.capture3(
62
68
  webpack_env,
@@ -67,12 +73,13 @@ class Webpacker::Compiler
67
73
  if status.success?
68
74
  logger.info "Compiled all packs in #{config.public_output_path}"
69
75
  logger.error "#{stderr}" unless stderr.empty?
70
- else
71
- logger.error "Compilation failed:\n#{stderr}"
72
- end
73
76
 
74
- if config.webpack_compile_output?
75
- logger.info stdout
77
+ if config.webpack_compile_output?
78
+ logger.info stdout
79
+ end
80
+ else
81
+ non_empty_streams = [stdout, stderr].delete_if(&:empty?)
82
+ logger.error "Compilation failed:\n#{non_empty_streams.join("\n\n")}"
76
83
  end
77
84
 
78
85
  status.success?
@@ -81,7 +88,7 @@ class Webpacker::Compiler
81
88
  def default_watched_paths
82
89
  [
83
90
  *config.resolved_paths_globbed,
84
- "#{config.source_path.relative_path_from(config.root_path)}/**/*",
91
+ config.source_path_globbed,
85
92
  "yarn.lock", "package.json",
86
93
  "config/webpack/**/*"
87
94
  ].freeze
@@ -23,12 +23,16 @@ class Webpacker::Configuration
23
23
  root_path.join(fetch(:source_path))
24
24
  end
25
25
 
26
+ def source_path_globbed
27
+ globbed_path_with_extensions(source_path.relative_path_from(root_path))
28
+ end
29
+
26
30
  def resolved_paths
27
31
  fetch(:resolved_paths)
28
32
  end
29
33
 
30
34
  def resolved_paths_globbed
31
- resolved_paths.map { |p| "#{p}/**/*" }
35
+ resolved_paths.map { |p| globbed_path_with_extensions(p) }
32
36
  end
33
37
 
34
38
  def source_entry_path
@@ -102,4 +106,8 @@ class Webpacker::Configuration
102
106
  @defaults ||= \
103
107
  HashWithIndifferentAccess.new(YAML.load_file(File.expand_path("../../install/config/webpacker.yml", __FILE__))[env])
104
108
  end
109
+
110
+ def globbed_path_with_extensions(path)
111
+ "#{path}/**/*{#{extensions.join(',')}}"
112
+ end
105
113
  end
@@ -32,7 +32,7 @@ class Webpacker::DevServer
32
32
 
33
33
  def https?
34
34
  case fetch(:https)
35
- when true, "true"
35
+ when true, "true", Hash
36
36
  true
37
37
  else
38
38
  false
@@ -5,16 +5,10 @@ class Webpacker::DevServerProxy < Rack::Proxy
5
5
 
6
6
  def initialize(app = nil, opts = {})
7
7
  @webpacker = opts.delete(:webpacker) || Webpacker.instance
8
+ opts[:streaming] = false if Rails.env.test? && !opts.key?(:streaming)
8
9
  super
9
10
  end
10
11
 
11
- def rewrite_response(response)
12
- _status, headers, _body = response
13
- headers.delete "transfer-encoding"
14
- headers.delete "content-length" if dev_server.running? && dev_server.https?
15
- response
16
- end
17
-
18
12
  def perform_request(env)
19
13
  if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && dev_server.running?
20
14
  env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = env["HTTP_X_FORWARDED_SERVER"] = dev_server.host_with_port
@@ -32,6 +26,6 @@ class Webpacker::DevServerProxy < Rack::Proxy
32
26
 
33
27
  private
34
28
  def public_output_uri_path
35
- config.public_output_path.relative_path_from(config.public_path)
29
+ config.public_output_path.relative_path_from(config.public_path).to_s + "/"
36
30
  end
37
31
  end