webpacker 4.1.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.node-version +1 -1
  3. data/.travis.yml +7 -20
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +8 -5
  7. data/README.md +45 -260
  8. data/docs/css.md +3 -4
  9. data/docs/deployment.md +2 -2
  10. data/docs/docker.md +17 -17
  11. data/docs/engines.md +13 -0
  12. data/docs/integrations.md +220 -0
  13. data/docs/typescript.md +2 -3
  14. data/lib/install/loaders/svelte.js +2 -2
  15. data/lib/install/template.rb +2 -2
  16. data/lib/tasks/webpacker/check_node.rake +14 -7
  17. data/lib/tasks/webpacker/check_yarn.rake +16 -9
  18. data/lib/tasks/webpacker/clean.rake +16 -6
  19. data/lib/tasks/webpacker/clobber.rake +8 -4
  20. data/lib/tasks/webpacker/compile.rake +1 -9
  21. data/lib/tasks/webpacker/yarn_install.rake +5 -16
  22. data/lib/webpacker.rb +8 -0
  23. data/lib/webpacker/commands.rb +45 -19
  24. data/lib/webpacker/dev_server_runner.rb +4 -4
  25. data/lib/webpacker/env.rb +1 -1
  26. data/lib/webpacker/manifest.rb +4 -4
  27. data/lib/webpacker/railtie.rb +3 -1
  28. data/lib/webpacker/version.rb +1 -1
  29. data/package.json +35 -35
  30. data/package/environments/__tests__/base.js +10 -0
  31. data/package/environments/base.js +12 -1
  32. data/package/environments/development.js +0 -4
  33. data/package/rules/sass.js +7 -1
  34. data/test/manifest_test.rb +37 -6
  35. data/test/rake_tasks_test.rb +11 -0
  36. data/test/test_app/app/javascript/packs/multi_entry.css +4 -0
  37. data/test/test_app/app/javascript/packs/multi_entry.js +4 -0
  38. data/webpacker.gemspec +4 -3
  39. data/yarn.lock +2624 -1544
  40. metadata +28 -12
  41. data/gemfiles/Gemfile-rails.4.2.x +0 -9
  42. data/gemfiles/Gemfile-rails.5.0.x +0 -9
  43. data/gemfiles/Gemfile-rails.5.1.x +0 -9
@@ -1,15 +1,25 @@
1
+ $stdout.sync = true
2
+
1
3
  require "webpacker/configuration"
2
4
 
3
5
  namespace :webpacker do
4
6
  desc "Remove old compiled webpacks"
5
- task :clean, [:keep] => ["webpacker:verify_install", :environment] do |_, args|
6
- Webpacker.clean(Integer(args.keep || 2))
7
+ task :clean, [:keep, :age] => ["webpacker:verify_install", :environment] do |_, args|
8
+ Webpacker.ensure_log_goes_to_stdout do
9
+ Webpacker.clean(Integer(args.keep || 2), Integer(args.age || 3600))
10
+ end
7
11
  end
8
12
  end
9
13
 
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
+ skip_webpacker_clean = %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])
15
+
16
+ unless skip_webpacker_clean
17
+ # Run clean if the assets:clean is run
18
+ if Rake::Task.task_defined?("assets:clean")
19
+ Rake::Task["assets:clean"].enhance do
20
+ Rake::Task["webpacker:clean"].invoke
21
+ end
22
+ else
23
+ Rake::Task.define_task("assets:clean" => "webpacker:clean")
14
24
  end
15
25
  end
@@ -8,9 +8,13 @@ namespace :webpacker do
8
8
  end
9
9
  end
10
10
 
11
- # Run clobber if the assets:clobber is run
12
- if Rake::Task.task_defined?("assets:clobber")
13
- Rake::Task["assets:clobber"].enhance do
14
- Rake::Task["webpacker:clobber"].invoke
11
+ skip_webpacker_clobber = %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])
12
+
13
+ unless skip_webpacker_clobber
14
+ # Run clobber if the assets:clobber is run
15
+ if Rake::Task.task_defined?("assets:clobber")
16
+ Rake::Task["assets:clobber"].enhance do
17
+ Rake::Task["webpacker:clobber"].invoke
18
+ end
15
19
  end
16
20
  end
@@ -1,13 +1,5 @@
1
1
  $stdout.sync = true
2
2
 
3
- def ensure_log_goes_to_stdout
4
- old_logger = Webpacker.logger
5
- Webpacker.logger = ActiveSupport::Logger.new(STDOUT)
6
- yield
7
- ensure
8
- Webpacker.logger = old_logger
9
- end
10
-
11
3
  def yarn_install_available?
12
4
  rails_major = Rails::VERSION::MAJOR
13
5
  rails_minor = Rails::VERSION::MINOR
@@ -27,7 +19,7 @@ namespace :webpacker do
27
19
  desc "Compile JavaScript packs using webpack for production with digests"
28
20
  task compile: ["webpacker:verify_install", :environment] do
29
21
  Webpacker.with_node_env(ENV.fetch("NODE_ENV", "production")) do
30
- ensure_log_goes_to_stdout do
22
+ Webpacker.ensure_log_goes_to_stdout do
31
23
  if Webpacker.compile
32
24
  # Successful compilation!
33
25
  else
@@ -1,21 +1,10 @@
1
1
  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
- system "yarn install --no-progress"
5
-
6
- exit(1) unless $?.success?
4
+ valid_node_envs = %w[test development production]
5
+ node_env = ENV.fetch("NODE_ENV") do
6
+ valid_node_envs.include?(Rails.env) ? Rails.env : "production"
7
+ end
8
+ system({ "NODE_ENV" => node_env }, "yarn install --no-progress --frozen-lockfile")
7
9
  end
8
10
  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
@@ -22,6 +22,14 @@ module Webpacker
22
22
  ENV["NODE_ENV"] = original
23
23
  end
24
24
 
25
+ def ensure_log_goes_to_stdout
26
+ old_logger = Webpacker.logger
27
+ Webpacker.logger = ActiveSupport::Logger.new(STDOUT)
28
+ yield
29
+ ensure
30
+ Webpacker.logger = old_logger
31
+ end
32
+
25
33
  delegate :logger, :logger=, :env, to: :instance
26
34
  delegate :config, :compiler, :manifest, :commands, :dev_server, to: :instance
27
35
  delegate :bootstrap, :clean, :clobber, :compile, to: :commands
@@ -1,25 +1,41 @@
1
1
  class Webpacker::Commands
2
- delegate :config, :compiler, :manifest, to: :@webpacker
2
+ delegate :config, :compiler, :manifest, :logger, to: :@webpacker
3
3
 
4
4
  def initialize(webpacker)
5
5
  @webpacker = webpacker
6
6
  end
7
7
 
8
- def clean(count_to_keep = 2)
8
+ # Cleanup old assets in the compile directory. By default it will
9
+ # keep the latest version, 2 backups created within the past hour.
10
+ #
11
+ # Examples
12
+ #
13
+ # To force only 1 backup to be kept, set count=1 and age=0.
14
+ #
15
+ # To only keep files created within the last 10 minutes, set count=0 and
16
+ # age=600.
17
+ #
18
+ def clean(count = 2, age = 3600)
9
19
  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 }
20
+ versions
21
+ .sort
22
+ .reverse
23
+ .each_with_index
24
+ .drop_while do |(mtime, _), index|
25
+ max_age = [0, Time.now - Time.at(mtime)].max
26
+ max_age < age && index < count
27
+ end
28
+ .each do |(_, files), index|
29
+ files.each do |file|
30
+ if File.file?(file)
31
+ File.delete(file)
32
+ logger.info "Removed #{file}"
33
+ end
34
+ end
35
+ end
22
36
  end
37
+
38
+ true
23
39
  end
24
40
 
25
41
  def clobber
@@ -38,11 +54,21 @@ class Webpacker::Commands
38
54
  end
39
55
 
40
56
  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)
57
+ def versions
58
+ all_files = Dir.glob("#{config.public_output_path}/**/*")
59
+ manifest_config = Dir.glob("#{config.public_manifest_path}*")
60
+
61
+ packs = all_files - manifest_config - current_version
62
+ packs.reject { |file| File.directory?(file) }.group_by { |file| File.mtime(file).utc.to_i }
63
+ end
64
+
65
+ def current_version
66
+ packs = manifest.refresh.values.map do |value|
67
+ next if value.is_a?(Hash)
68
+
69
+ File.join(config.root_path, "public", "#{value}*")
70
+ end.compact
44
71
 
45
- File.join(config.root_path, "public", value)
46
- end.flatten
72
+ Dir.glob(packs).uniq
47
73
  end
48
74
  end
@@ -16,20 +16,20 @@ module Webpacker
16
16
  def load_config
17
17
  app_root = Pathname.new(@app_path)
18
18
 
19
- config = Configuration.new(
19
+ @config = Configuration.new(
20
20
  root_path: app_root,
21
21
  config_path: app_root.join("config/webpacker.yml"),
22
22
  env: ENV["RAILS_ENV"]
23
23
  )
24
24
 
25
- dev_server = DevServer.new(config)
25
+ dev_server = DevServer.new(@config)
26
26
 
27
27
  @hostname = dev_server.host
28
28
  @port = dev_server.port
29
29
  @pretty = dev_server.pretty?
30
30
 
31
31
  rescue Errno::ENOENT, NoMethodError
32
- $stdout.puts "webpack dev_server configuration not found in #{config.config_path}[#{ENV["RAILS_ENV"]}]."
32
+ $stdout.puts "webpack dev_server configuration not found in #{@config.config_path}[#{ENV["RAILS_ENV"]}]."
33
33
  $stdout.puts "Please run bundle exec rails webpacker:install to install Webpacker"
34
34
  exit!
35
35
  end
@@ -39,7 +39,7 @@ module Webpacker
39
39
  server.close
40
40
 
41
41
  rescue Errno::EADDRINUSE
42
- $stdout.puts "Another program is running on port #{@port}. Set a new port in #{@config_file} for dev_server"
42
+ $stdout.puts "Another program is running on port #{@port}. Set a new port in #{@config.config_path} for dev_server"
43
43
  exit!
44
44
  end
45
45
 
@@ -12,7 +12,7 @@ class Webpacker::Env
12
12
  end
13
13
 
14
14
  def inquire
15
- fallback_env_warning unless current
15
+ fallback_env_warning if config_path.exist? && !current
16
16
  current || DEFAULT.inquiry
17
17
  end
18
18
 
@@ -29,7 +29,7 @@ class Webpacker::Manifest
29
29
  end
30
30
 
31
31
  def lookup_pack_with_chunks!(name, pack_type = {})
32
- lookup_pack_with_chunks(name, pack_type) || handle_missing_entry(name)
32
+ lookup_pack_with_chunks(name, pack_type) || handle_missing_entry(name, pack_type)
33
33
  end
34
34
 
35
35
  # Computes the relative path for a given Webpacker asset using manifest.json.
@@ -46,7 +46,7 @@ class Webpacker::Manifest
46
46
 
47
47
  # Like lookup, except that if no asset is found, raises a Webpacker::Manifest::MissingEntryError.
48
48
  def lookup!(name, pack_type = {})
49
- lookup(name, pack_type) || handle_missing_entry(name)
49
+ lookup(name, pack_type) || handle_missing_entry(name, pack_type)
50
50
  end
51
51
 
52
52
  private
@@ -75,8 +75,8 @@ class Webpacker::Manifest
75
75
  "#{name}.#{manifest_type(pack_type)}"
76
76
  end
77
77
 
78
- def handle_missing_entry(name)
79
- raise Webpacker::Manifest::MissingEntryError, missing_file_from_manifest_error(name)
78
+ def handle_missing_entry(name, pack_type)
79
+ raise Webpacker::Manifest::MissingEntryError, missing_file_from_manifest_error(full_pack_name(name, pack_type[:type]))
80
80
  end
81
81
 
82
82
  def load
@@ -91,6 +91,8 @@ class Webpacker::Engine < ::Rails::Engine
91
91
  end
92
92
 
93
93
  initializer "webpacker.set_source" do |app|
94
- app.config.javascript_path = Webpacker.config.source_path.relative_path_from(Rails.root.join("app")).to_s
94
+ if Webpacker.config.config_path.exist?
95
+ app.config.javascript_path = Webpacker.config.source_path.relative_path_from(Rails.root.join("app")).to_s
96
+ end
95
97
  end
96
98
  end
@@ -1,4 +1,4 @@
1
1
  module Webpacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "4.1.0".freeze
3
+ VERSION = "5.0.1".freeze
4
4
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rails/webpacker",
3
- "version": "4.1.0",
3
+ "version": "5.0.1",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "main": "package/index.js",
6
6
  "files": [
@@ -8,56 +8,56 @@
8
8
  "lib/install/config/webpacker.yml"
9
9
  ],
10
10
  "engines": {
11
- "node": ">=8.16.0",
12
- "yarn": ">=1.0.0"
11
+ "node": ">=10.13.0",
12
+ "yarn": ">=1 <2"
13
13
  },
14
14
  "dependencies": {
15
- "@babel/core": "^7.7.2",
16
- "@babel/plugin-proposal-class-properties": "^7.7.0",
17
- "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
18
- "@babel/plugin-syntax-dynamic-import": "^7.2.0",
19
- "@babel/plugin-transform-destructuring": "^7.6.0",
20
- "@babel/plugin-transform-regenerator": "^7.7.0",
21
- "@babel/plugin-transform-runtime": "^7.6.2",
22
- "@babel/preset-env": "^7.7.1",
23
- "@babel/runtime": "^7.7.2",
24
- "babel-loader": "^8.0.6",
15
+ "@babel/core": "^7.9.0",
16
+ "@babel/plugin-proposal-class-properties": "^7.8.3",
17
+ "@babel/plugin-proposal-object-rest-spread": "^7.9.0",
18
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
19
+ "@babel/plugin-transform-destructuring": "^7.8.8",
20
+ "@babel/plugin-transform-regenerator": "^7.8.7",
21
+ "@babel/plugin-transform-runtime": "^7.9.0",
22
+ "@babel/preset-env": "^7.9.0",
23
+ "@babel/runtime": "^7.9.2",
24
+ "babel-loader": "^8.1.0",
25
25
  "babel-plugin-dynamic-import-node": "^2.3.0",
26
- "babel-plugin-macros": "^2.6.1",
27
- "case-sensitive-paths-webpack-plugin": "^2.2.0",
28
- "compression-webpack-plugin": "^3.0.0",
29
- "core-js": "^3.4.0",
30
- "css-loader": "^3.2.0",
31
- "file-loader": "^4.2.0",
26
+ "babel-plugin-macros": "^2.8.0",
27
+ "case-sensitive-paths-webpack-plugin": "^2.3.0",
28
+ "compression-webpack-plugin": "^3.1.0",
29
+ "core-js": "^3.6.4",
30
+ "css-loader": "^3.4.2",
31
+ "file-loader": "^6.0.0",
32
32
  "flatted": "^2.0.1",
33
33
  "glob": "^7.1.6",
34
34
  "js-yaml": "^3.13.1",
35
- "mini-css-extract-plugin": "^0.8.0",
36
- "node-sass": "^4.13.0",
35
+ "mini-css-extract-plugin": "^0.9.0",
36
+ "node-sass": "^4.13.1",
37
37
  "optimize-css-assets-webpack-plugin": "^5.0.3",
38
38
  "path-complete-extname": "^1.0.0",
39
- "pnp-webpack-plugin": "^1.5.0",
40
- "postcss-flexbugs-fixes": "^4.1.0",
39
+ "pnp-webpack-plugin": "^1.6.4",
40
+ "postcss-flexbugs-fixes": "^4.2.0",
41
41
  "postcss-import": "^12.0.1",
42
42
  "postcss-loader": "^3.0.0",
43
43
  "postcss-preset-env": "^6.7.0",
44
- "postcss-safe-parser": "^4.0.1",
45
- "regenerator-runtime": "^0.13.3",
46
- "sass-loader": "7.3.1",
47
- "style-loader": "^1.0.0",
48
- "terser-webpack-plugin": "^2.2.1",
49
- "webpack": "^4.41.2",
44
+ "postcss-safe-parser": "^4.0.2",
45
+ "regenerator-runtime": "^0.13.5",
46
+ "sass-loader": "^8.0.2",
47
+ "style-loader": "^1.1.3",
48
+ "terser-webpack-plugin": "^2.3.5",
49
+ "webpack": "^4.42.1",
50
50
  "webpack-assets-manifest": "^3.1.1",
51
- "webpack-cli": "^3.3.10",
51
+ "webpack-cli": "^3.3.11",
52
52
  "webpack-sources": "^1.4.3"
53
53
  },
54
54
  "devDependencies": {
55
- "eslint": "^6.6.0",
56
- "eslint-config-airbnb": "^18.0.1",
57
- "eslint-plugin-import": "^2.18.2",
55
+ "eslint": "^6.8.0",
56
+ "eslint-config-airbnb": "^18.1.0",
57
+ "eslint-plugin-import": "^2.20.1",
58
58
  "eslint-plugin-jsx-a11y": "^6.2.3",
59
- "eslint-plugin-react": "^7.16.0",
60
- "jest": "^24.9.0"
59
+ "eslint-plugin-react": "^7.19.0",
60
+ "jest": "^25.1.0"
61
61
  },
62
62
  "jest": {
63
63
  "testRegex": "(/__tests__/.*|(\\.|/))\\.jsx?$",
@@ -29,6 +29,16 @@ describe('Environment', () => {
29
29
  )
30
30
  })
31
31
 
32
+ test('should return multi file entry points', () => {
33
+ const config = environment.toWebpackConfig()
34
+ expect(config.entry.multi_entry.sort()).toEqual(
35
+ [
36
+ resolve('app', 'javascript', 'packs', 'multi_entry.css'),
37
+ resolve('app', 'javascript', 'packs', 'multi_entry.js')
38
+ ]
39
+ )
40
+ })
41
+
32
42
  test('should return output', () => {
33
43
  const config = environment.toWebpackConfig()
34
44
  expect(config.output.filename).toEqual('js/[name]-[contenthash].js')
@@ -65,7 +65,18 @@ const getEntryObject = () => {
65
65
  paths.forEach((path) => {
66
66
  const namespace = relative(join(rootPath), dirname(path))
67
67
  const name = join(namespace, basename(path, extname(path)))
68
- result.set(name, resolve(path))
68
+ let assetPaths = resolve(path)
69
+
70
+ // Allows for multiple filetypes per entry (https://webpack.js.org/guides/entry-advanced/)
71
+ // Transforms the config object value to an array with all values under the same name
72
+ let previousPaths = result.get(name)
73
+ if (previousPaths) {
74
+ previousPaths = Array.isArray(previousPaths) ? previousPaths : [previousPaths]
75
+ previousPaths.push(assetPaths)
76
+ assetPaths = previousPaths
77
+ }
78
+
79
+ result.set(name, assetPaths)
69
80
  })
70
81
  return result
71
82
  }
@@ -14,11 +14,7 @@ module.exports = class extends Base {
14
14
 
15
15
  this.config.merge({
16
16
  mode: 'development',
17
- cache: true,
18
17
  devtool: 'cheap-module-source-map',
19
- output: {
20
- pathinfo: true
21
- },
22
18
  devServer: {
23
19
  clientLogLevel: 'none',
24
20
  compress: devServer.compress,
@@ -1,8 +1,14 @@
1
1
  const getStyleRule = require('../utils/get_style_rule')
2
+ const { resolved_paths: includePaths } = require('../config')
2
3
 
3
4
  module.exports = getStyleRule(/\.(scss|sass)(\.erb)?$/i, false, [
4
5
  {
5
6
  loader: 'sass-loader',
6
- options: { sourceMap: true }
7
+ options: {
8
+ sourceMap: true,
9
+ sassOptions: {
10
+ includePaths
11
+ }
12
+ }
7
13
  }
8
14
  ])