webpacker 6.0.0.beta.7 → 6.0.0.rc.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/jest.yml +5 -2
  3. data/.github/workflows/js-lint.yml +4 -1
  4. data/.github/workflows/rubocop.yml +1 -1
  5. data/.github/workflows/ruby.yml +12 -12
  6. data/.node-version +1 -1
  7. data/.rubocop.yml +1 -0
  8. data/CHANGELOG.md +21 -10
  9. data/Gemfile.lock +83 -83
  10. data/README.md +160 -157
  11. data/config/webpacker.yml +1 -1
  12. data/docs/developing_webpacker.md +29 -0
  13. data/docs/troubleshooting.md +53 -23
  14. data/docs/v6_upgrade.md +31 -42
  15. data/gemfiles/Gemfile-rails-edge +1 -1
  16. data/gemfiles/Gemfile-rails.6.1.x +12 -0
  17. data/lib/install/bin/yarn +18 -0
  18. data/lib/install/config/webpacker.yml +14 -17
  19. data/lib/install/package.json +17 -0
  20. data/lib/install/packs/entrypoints/application.js +3 -4
  21. data/lib/install/template.rb +28 -15
  22. data/lib/tasks/webpacker/check_node.rake +3 -1
  23. data/lib/tasks/webpacker/check_yarn.rake +4 -2
  24. data/lib/tasks/webpacker/clobber.rake +1 -1
  25. data/lib/tasks/webpacker/verify_config.rake +14 -0
  26. data/lib/tasks/webpacker/verify_install.rake +1 -11
  27. data/lib/tasks/yarn.rake +36 -0
  28. data/lib/webpacker/commands.rb +19 -15
  29. data/lib/webpacker/configuration.rb +15 -4
  30. data/lib/webpacker/dev_server.rb +6 -0
  31. data/lib/webpacker/dev_server_runner.rb +6 -3
  32. data/lib/webpacker/env.rb +5 -1
  33. data/lib/webpacker/helper.rb +14 -8
  34. data/lib/webpacker/instance.rb +4 -0
  35. data/lib/webpacker/manifest.rb +1 -2
  36. data/lib/webpacker/railtie.rb +1 -2
  37. data/lib/webpacker/runner.rb +1 -1
  38. data/lib/webpacker/version.rb +1 -1
  39. data/lib/webpacker.rb +1 -1
  40. data/package/__tests__/development.js +4 -4
  41. data/package/__tests__/env.js +8 -4
  42. data/package/babel/preset.js +0 -1
  43. data/package/config.js +3 -3
  44. data/package/env.js +6 -3
  45. data/package/environments/__tests__/base.js +3 -3
  46. data/package/environments/base.js +12 -7
  47. data/package/environments/development.js +37 -33
  48. data/package/index.js +2 -0
  49. data/package/inliningCss.js +7 -0
  50. data/package/rules/file.js +1 -1
  51. data/package/rules/sass.js +1 -2
  52. data/package/utils/get_style_rule.js +4 -2
  53. data/package.json +25 -29
  54. data/test/command_test.rb +76 -0
  55. data/test/configuration_test.rb +1 -1
  56. data/test/dev_server_runner_test.rb +5 -2
  57. data/test/helper_test.rb +48 -34
  58. data/test/manifest_test.rb +10 -2
  59. data/test/mounted_app/test/dummy/config/webpacker.yml +1 -1
  60. data/test/test_app/config/webpacker.yml +1 -3
  61. data/test/test_app/config/webpacker_other_location.yml +79 -0
  62. data/test/test_app/public/packs/manifest.json +12 -5
  63. data/test/webpacker_test.rb +17 -0
  64. data/webpacker.gemspec +1 -1
  65. data/yarn.lock +2202 -2680
  66. metadata +18 -9
@@ -42,7 +42,6 @@ module.exports = function config(api) {
42
42
  ]
43
43
  ].filter(Boolean),
44
44
  plugins: [
45
- 'babel-plugin-macros',
46
45
  ['@babel/plugin-proposal-class-properties', { loose: true }],
47
46
  ['@babel/plugin-transform-runtime', { helpers: false }],
48
47
  isProductionEnv &&
data/package/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { resolve } = require('path')
2
- const { safeLoad } = require('js-yaml')
2
+ const { load } = require('js-yaml')
3
3
  const { readFileSync } = require('fs')
4
4
  const { merge } = require('webpack-merge')
5
5
  const { ensureTrailingSlash } = require('./utils/helpers')
@@ -9,12 +9,12 @@ const configPath = require('./configPath')
9
9
  const defaultConfigPath = require.resolve('../lib/install/config/webpacker.yml')
10
10
 
11
11
  const getDefaultConfig = () => {
12
- const defaultConfig = safeLoad(readFileSync(defaultConfigPath), 'utf8')
12
+ const defaultConfig = load(readFileSync(defaultConfigPath), 'utf8')
13
13
  return defaultConfig[railsEnv] || defaultConfig.production
14
14
  }
15
15
 
16
16
  const defaults = getDefaultConfig()
17
- const app = safeLoad(readFileSync(configPath), 'utf8')[railsEnv]
17
+ const app = load(readFileSync(configPath), 'utf8')[railsEnv]
18
18
 
19
19
  const config = merge(defaults, app)
20
20
  config.outputPath = resolve(config.public_root_path, config.public_output_path)
data/package/env.js CHANGED
@@ -1,4 +1,4 @@
1
- const { safeLoad } = require('js-yaml')
1
+ const { load } = require('js-yaml')
2
2
  const { readFileSync } = require('fs')
3
3
 
4
4
  const NODE_ENVIRONMENTS = ['development', 'production', 'test']
@@ -12,13 +12,16 @@ const nodeEnv
12
12
  const isProduction = nodeEnv === 'production'
13
13
  const isDevelopment = nodeEnv === 'development'
14
14
 
15
- const config = safeLoad(readFileSync(configPath), 'utf8')
15
+ const config = load(readFileSync(configPath), 'utf8')
16
16
  const availableEnvironments = Object.keys(config).join('|')
17
17
  const regex = new RegExp(`^(${availableEnvironments})$`, 'g')
18
18
 
19
+ const runningWebpackDevServer = process.env.WEBPACK_SERVE === 'true'
20
+
19
21
  module.exports = {
20
22
  railsEnv: railsEnv && railsEnv.match(regex) ? railsEnv : DEFAULT,
21
23
  nodeEnv,
22
24
  isProduction,
23
- isDevelopment
25
+ isDevelopment,
26
+ runningWebpackDevServer
24
27
  }
@@ -29,9 +29,9 @@ describe('Base config', () => {
29
29
  })
30
30
 
31
31
  test('should return output', () => {
32
- expect(baseConfig.output.filename).toEqual('js/[name]-[contenthash].js')
32
+ expect(baseConfig.output.filename).toEqual('js/[name].js')
33
33
  expect(baseConfig.output.chunkFilename).toEqual(
34
- 'js/[name]-[contenthash].chunk.js'
34
+ 'js/[name].chunk.js'
35
35
  )
36
36
  })
37
37
 
@@ -44,7 +44,7 @@ describe('Base config', () => {
44
44
  })
45
45
 
46
46
  test('should return default plugins', () => {
47
- expect(baseConfig.plugins.length).toEqual(3)
47
+ expect(baseConfig.plugins.length).toEqual(2)
48
48
  })
49
49
 
50
50
  test('should return default resolveLoader', () => {
@@ -5,10 +5,10 @@ const { basename, dirname, join, relative, resolve } = require('path')
5
5
  const extname = require('path-complete-extname')
6
6
  const PnpWebpackPlugin = require('pnp-webpack-plugin')
7
7
  const { sync: globSync } = require('glob')
8
- const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
9
8
  const WebpackAssetsManifest = require('webpack-assets-manifest')
10
9
  const webpack = require('webpack')
11
10
  const rules = require('../rules')
11
+ const { isProduction } = require('../env')
12
12
  const config = require('../config')
13
13
  const { moduleExists } = require('../utils/helpers')
14
14
 
@@ -52,7 +52,6 @@ const getModulePaths = () => {
52
52
  const getPlugins = () => {
53
53
  const plugins = [
54
54
  new webpack.EnvironmentPlugin(process.env),
55
- new CaseSensitivePathsPlugin(),
56
55
  new WebpackAssetsManifest({
57
56
  entrypoints: true,
58
57
  writeToDisk: true,
@@ -63,11 +62,12 @@ const getPlugins = () => {
63
62
  ]
64
63
 
65
64
  if (moduleExists('css-loader') && moduleExists('mini-css-extract-plugin')) {
65
+ const hash = isProduction ? '-[contenthash:8]' : ''
66
66
  const MiniCssExtractPlugin = require('mini-css-extract-plugin')
67
67
  plugins.push(
68
68
  new MiniCssExtractPlugin({
69
- filename: 'css/[name]-[contenthash:8].css',
70
- chunkFilename: 'css/[id]-[contenthash:8].css'
69
+ filename: `css/[name]${hash}.css`,
70
+ chunkFilename: `css/[id]${hash}.css`
71
71
  })
72
72
  )
73
73
  }
@@ -75,12 +75,17 @@ const getPlugins = () => {
75
75
  return plugins
76
76
  }
77
77
 
78
+ // Don't use contentHash except for production for performance
79
+ // https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
80
+ const hash = isProduction ? '-[contenthash]' : ''
78
81
  module.exports = {
79
82
  mode: 'production',
80
83
  output: {
81
- filename: 'js/[name]-[contenthash].js',
82
- chunkFilename: 'js/[name]-[contenthash].chunk.js',
83
- hotUpdateChunkFilename: 'js/[id]-[hash].hot-update.js',
84
+ filename: `js/[name]${hash}.js`,
85
+ chunkFilename: `js/[name]${hash}.chunk.js`,
86
+
87
+ // https://webpack.js.org/configuration/output/#outputhotupdatechunkfilename
88
+ hotUpdateChunkFilename: 'js/[id].[fullhash].hot-update.js',
84
89
  path: config.outputPath,
85
90
  publicPath: config.publicPath
86
91
  },
@@ -1,8 +1,8 @@
1
1
  const { merge } = require('webpack-merge')
2
- const webpack = require('webpack')
3
2
 
4
3
  const baseConfig = require('./base')
5
4
  const devServer = require('../dev_server')
5
+ const { runningWebpackDevServer } = require('../env')
6
6
 
7
7
  const { outputPath: contentBase, publicPath } = require('../config')
8
8
 
@@ -11,44 +11,48 @@ let devConfig = {
11
11
  devtool: 'cheap-module-source-map'
12
12
  }
13
13
 
14
- if (
15
- process.env.WEBPACK_DEV_SERVER &&
16
- process.env.WEBPACK_DEV_SERVER !== 'undefined'
17
- ) {
14
+ if (runningWebpackDevServer) {
18
15
  if (devServer.hmr) {
19
16
  devConfig = merge(devConfig, {
20
- output: { filename: '[name]-[hash].js' },
21
- plugins: [new webpack.HotModuleReplacementPlugin()]
17
+ output: { filename: '[name]-[hash].js' }
22
18
  })
23
19
  }
24
20
 
25
- devConfig = merge(devConfig, {
26
- devServer: {
27
- clientLogLevel: 'none',
28
- compress: devServer.compress,
29
- quiet: devServer.quiet,
30
- disableHostCheck: devServer.disable_host_check,
31
- host: devServer.host,
32
- port: devServer.port,
33
- https: devServer.https,
34
- hot: devServer.hmr,
35
- contentBase,
36
- inline: devServer.inline,
37
- injectClient: devServer.inject_client,
38
- useLocalIp: devServer.use_local_ip,
39
- public: devServer.public,
40
- publicPath,
41
- historyApiFallback: { disableDotRule: true },
42
- headers: devServer.headers,
43
- overlay: devServer.overlay,
44
- stats: {
45
- entrypoints: false,
46
- errorDetails: true,
47
- modules: false,
48
- moduleTrace: false
49
- },
50
- watchOptions: devServer.watch_options
21
+ const devServerConfig = {
22
+ devMiddleware: {
23
+ publicPath
24
+ },
25
+ compress: devServer.compress,
26
+ allowedHosts: devServer.allowed_hosts,
27
+ host: devServer.host,
28
+ port: devServer.port,
29
+ https: devServer.https,
30
+ hot: devServer.hmr,
31
+ liveReload: !devServer.hmr,
32
+ historyApiFallback: { disableDotRule: true },
33
+ headers: devServer.headers,
34
+ static: {
35
+ publicPath: contentBase
51
36
  }
37
+ }
38
+
39
+ if (devServer.static) {
40
+ devServerConfig.static = { ...devServerConfig.static, ...devServer.static }
41
+ }
42
+
43
+ if (devServer.client) {
44
+ devServerConfig.client = devServer.client
45
+ }
46
+
47
+ devConfig = merge(devConfig, {
48
+ stats: {
49
+ colors: true,
50
+ entrypoints: false,
51
+ errorDetails: true,
52
+ modules: false,
53
+ moduleTrace: false
54
+ },
55
+ devServer: devServerConfig
52
56
  })
53
57
  }
54
58
 
data/package/index.js CHANGED
@@ -10,6 +10,7 @@ const config = require('./config')
10
10
  const devServer = require('./dev_server')
11
11
  const { nodeEnv } = require('./env')
12
12
  const { moduleExists, canProcess } = require('./utils/helpers')
13
+ const inliningCss = require('./inliningCss')
13
14
 
14
15
  const webpackConfig = () => {
15
16
  const path = resolve(__dirname, 'environments', `${nodeEnv}.js`)
@@ -25,5 +26,6 @@ module.exports = {
25
26
  rules,
26
27
  moduleExists,
27
28
  canProcess,
29
+ inliningCss,
28
30
  ...webpackMerge
29
31
  }
@@ -0,0 +1,7 @@
1
+ const { runningWebpackDevServer } = require('./env')
2
+ const devServer = require('./dev_server')
3
+
4
+ // This logic is tied to lib/webpacker/instance.rb
5
+ const inliningCss = runningWebpackDevServer && devServer.hmr
6
+
7
+ module.exports = inliningCss
@@ -18,6 +18,6 @@ module.exports = {
18
18
  exclude: [/\.(js|mjs|jsx|ts|tsx)$/],
19
19
  type: 'asset/resource',
20
20
  generator: {
21
- filename: 'media/images/[hash][ext][query]'
21
+ filename: 'static/[hash][ext][query]'
22
22
  }
23
23
  }
@@ -9,8 +9,7 @@ module.exports = canProcess('sass-loader', (resolvedPath) =>
9
9
  {
10
10
  loader: resolvedPath,
11
11
  options: {
12
- sassOptions: { includePaths },
13
- implementation: require('sass')
12
+ sassOptions: { includePaths }
14
13
  }
15
14
  }
16
15
  ])
@@ -1,6 +1,6 @@
1
1
  /* eslint global-require: 0 */
2
-
3
2
  const { canProcess, moduleExists } = require('./helpers')
3
+ const inliningCss = require('../inliningCss')
4
4
 
5
5
  const getStyleRule = (test, preprocessors = []) => {
6
6
  if (moduleExists('css-loader')) {
@@ -10,8 +10,10 @@ const getStyleRule = (test, preprocessors = []) => {
10
10
  options: { sourceMap: true }
11
11
  }))
12
12
 
13
+ // style-loader is required when using css modules with HMR on the webpack-dev-server
14
+
13
15
  const use = [
14
- { loader: require('mini-css-extract-plugin').loader },
16
+ inliningCss ? 'style-loader' : require('mini-css-extract-plugin').loader,
15
17
  {
16
18
  loader: require.resolve('css-loader'),
17
19
  options: {
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rails/webpacker",
3
- "version": "6.0.0-beta.7",
3
+ "version": "6.0.0-rc.5",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "main": "package/index.js",
6
6
  "files": [
@@ -8,40 +8,36 @@
8
8
  "lib/install/config/webpacker.yml"
9
9
  ],
10
10
  "engines": {
11
- "node": ">=10.22.1 || ^12 || >=14",
12
- "yarn": ">=1 <3"
11
+ "node": ">= 12.13.0 || >=14",
12
+ "yarn": ">=1 <4"
13
13
  },
14
14
  "dependencies": {
15
- "@babel/core": "^7.12.9",
16
- "@babel/plugin-proposal-class-properties": "^7.12.1",
17
- "@babel/plugin-transform-runtime": "^7.12.1",
18
- "@babel/preset-env": "^7.12.11",
19
- "@babel/runtime": "^7.12.5",
15
+ "@babel/core": "^7.15.0",
16
+ "@babel/plugin-proposal-class-properties": "^7.14.5",
17
+ "@babel/plugin-transform-runtime": "^7.15.0",
18
+ "@babel/preset-env": "^7.15.0",
19
+ "@babel/runtime": "^7.15.3",
20
20
  "babel-loader": "^8.2.2",
21
- "babel-plugin-macros": "^3.0.1",
22
- "case-sensitive-paths-webpack-plugin": "^2.3.0",
23
- "compression-webpack-plugin": "^7.1.0",
24
- "core-js": "^3.8.0",
25
- "glob": "^7.1.6",
26
- "js-yaml": "^3.14.0",
21
+ "compression-webpack-plugin": "^8.0.1",
22
+ "glob": "^7.1.7",
23
+ "js-yaml": "^4.1.0",
27
24
  "path-complete-extname": "^1.0.0",
28
- "pnp-webpack-plugin": "^1.6.4",
29
- "regenerator-runtime": "^0.13.7",
30
- "terser-webpack-plugin": "^5.0.3",
31
- "webpack": "^5.11.0",
32
- "webpack-assets-manifest": "^5.0.0",
33
- "webpack-cli": "^4.2.0",
34
- "webpack-merge": "^5.7.2",
35
- "webpack-sources": "^2.2.0"
25
+ "pnp-webpack-plugin": "^1.7.0",
26
+ "terser-webpack-plugin": "^5.1.4",
27
+ "webpack": "^5.51.1",
28
+ "webpack-assets-manifest": "^5.0.6",
29
+ "webpack-cli": "^4.8.0",
30
+ "webpack-merge": "^5.8.0",
31
+ "webpack-sources": "^3.2.0"
36
32
  },
37
33
  "devDependencies": {
38
- "eslint": "^7.16.0",
39
- "eslint-config-airbnb": "^18.2.0",
40
- "eslint-config-prettier": "^7.1.0",
41
- "eslint-plugin-import": "^2.22.1",
42
- "eslint-plugin-jsx-a11y": "^6.3.1",
43
- "eslint-plugin-react": "^7.21.4",
44
- "jest": "^26.5.3"
34
+ "eslint": "^7.32.0",
35
+ "eslint-config-airbnb": "^18.2.1",
36
+ "eslint-config-prettier": "^8.3.0",
37
+ "eslint-plugin-import": "^2.24.1",
38
+ "eslint-plugin-jsx-a11y": "^6.4.1",
39
+ "eslint-plugin-react": "^7.24.0",
40
+ "jest": "^27.0.6"
45
41
  },
46
42
  "jest": {
47
43
  "testRegex": "(/__tests__/.*|(\\.|/))\\.jsx?$",
data/test/command_test.rb CHANGED
@@ -31,3 +31,79 @@ class CommandTest < Minitest::Test
31
31
  end
32
32
  end
33
33
  end
34
+
35
+ class ClearCommandVersioningTest < Minitest::Test
36
+ def setup
37
+ @now = Time.parse("2021-01-01 12:34:56 UTC")
38
+ # Test assets to be kept and deleted, path and mtime
39
+ @prev_files = {
40
+ # recent versions to be kept with Webpacker.commands.clean(count = 2)
41
+ "js/application-deadbeef.js" => @now - 4000,
42
+ "js/common-deadbeee.js" => @now - 4002,
43
+ "css/common-deadbeed.css" => @now - 4004,
44
+ "media/images/logo-deadbeeb.css" => @now - 4006,
45
+ "js/application-1eadbeef.js" => @now - 8000,
46
+ "js/common-1eadbeee.js" => @now - 8002,
47
+ "css/common-1eadbeed.css" => @now - 8004,
48
+ "media/images/logo-1eadbeeb.css" => @now - 8006,
49
+ # new files to be kept with Webpacker.commands.clean(age = 3600)
50
+ "js/brandnew-0001.js" => @now,
51
+ "js/brandnew-0002.js" => @now - 10,
52
+ "js/brandnew-0003.js" => @now - 20,
53
+ "js/brandnew-0004.js" => @now - 40,
54
+ }.transform_keys { |path| "#{Webpacker.config.public_output_path}/#{path}" }
55
+ @expired_files = {
56
+ # old files that are outside count = 2 or age = 3600 and to be deleted
57
+ "js/application-0eadbeef.js" => @now - 9000,
58
+ "js/common-0eadbeee.js" => @now - 9002,
59
+ "css/common-0eadbeed.css" => @now - 9004,
60
+ "js/brandnew-0005.js" => @now - 3640,
61
+ }.transform_keys { |path| "#{Webpacker.config.public_output_path}/#{path}" }
62
+ @all_files = @prev_files.merge(@expired_files)
63
+ @dir_glob_stub = Proc.new { |arg|
64
+ case arg
65
+ when "#{Webpacker.config.public_output_path}/**/*"
66
+ @all_files.keys
67
+ else
68
+ []
69
+ end
70
+ }
71
+ @file_mtime_stub = Proc.new { |longpath|
72
+ @all_files[longpath]
73
+ }
74
+ @file_delete_mock = Minitest::Mock.new
75
+ @expired_files.keys.each do |longpath|
76
+ @file_delete_mock.expect(:delete, 1, [longpath])
77
+ end
78
+ @file_delete_stub = Proc.new { |longpath|
79
+ if @prev_files.has_key?(longpath)
80
+ flunk "#{longpath} should not be deleted"
81
+ else
82
+ @file_delete_mock.delete(longpath)
83
+ end
84
+ }
85
+ end
86
+
87
+ def time_and_files_stub(&proc)
88
+ Time.stub :now, @now do
89
+ Dir.stub :glob, @dir_glob_stub do
90
+ File.stub :directory?, false do
91
+ File.stub :file?, true do
92
+ File.stub :mtime, @file_mtime_stub do
93
+ File.stub :delete, @file_delete_stub do
94
+ yield proc
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ @file_delete_mock.verify
102
+ end
103
+
104
+ def test_clean_command_with_versioned_files
105
+ time_and_files_stub do
106
+ assert Webpacker.commands.clean
107
+ end
108
+ end
109
+ end
@@ -44,7 +44,7 @@ class ConfigurationTest < Webpacker::Test
44
44
  end
45
45
 
46
46
  def test_cache_path
47
- cache_path = File.expand_path File.join(File.dirname(__FILE__), "test_app/tmp/cache/webpacker").to_s
47
+ cache_path = File.expand_path File.join(File.dirname(__FILE__), "test_app/tmp/webpacker").to_s
48
48
  assert_equal @config.cache_path.to_s, cache_path
49
49
  end
50
50