webpacker 4.0.7 → 4.1.0

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