webpacker 4.0.0.pre.3 → 4.0.0.rc.1

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.node-version +1 -1
  3. data/.travis.yml +6 -4
  4. data/CHANGELOG.md +38 -0
  5. data/CONTRIBUTING.md +33 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +64 -59
  8. data/README.md +16 -4
  9. data/docs/assets.md +7 -4
  10. data/docs/css.md +51 -8
  11. data/docs/es6.md +1 -1
  12. data/docs/troubleshooting.md +1 -1
  13. data/docs/webpack.md +42 -1
  14. data/gemfiles/Gemfile-rails-edge +1 -1
  15. data/gemfiles/Gemfile-rails.4.2.x +1 -1
  16. data/gemfiles/Gemfile-rails.5.0.x +1 -1
  17. data/gemfiles/Gemfile-rails.5.1.x +1 -1
  18. data/gemfiles/Gemfile-rails.5.2.x +1 -1
  19. data/lib/install/coffee.rb +2 -2
  20. data/lib/install/config/babel.config.js +70 -0
  21. data/lib/install/config/postcss.config.js +12 -0
  22. data/lib/install/config/webpacker.yml +22 -0
  23. data/lib/install/elm.rb +2 -2
  24. data/lib/install/erb.rb +2 -2
  25. data/lib/install/examples/elm/Main.elm +5 -4
  26. data/lib/install/examples/elm/hello_elm.js +6 -2
  27. data/lib/install/examples/react/babel.config.js +83 -0
  28. data/lib/install/examples/vue/hello_vue.js +6 -4
  29. data/lib/install/loaders/typescript.js +8 -3
  30. data/lib/install/react.rb +6 -20
  31. data/lib/install/template.rb +5 -4
  32. data/lib/install/typescript.rb +3 -3
  33. data/lib/install/vue.rb +4 -4
  34. data/lib/tasks/webpacker/compile.rake +3 -5
  35. data/lib/tasks/webpacker/yarn_install.rake +1 -1
  36. data/lib/webpacker/compiler.rb +4 -13
  37. data/lib/webpacker/configuration.rb +8 -0
  38. data/lib/webpacker/dev_server.rb +0 -9
  39. data/lib/webpacker/dev_server_runner.rb +12 -5
  40. data/lib/webpacker/helper.rb +10 -14
  41. data/lib/webpacker/manifest.rb +58 -21
  42. data/lib/webpacker/railtie.rb +3 -2
  43. data/lib/webpacker/version.rb +1 -1
  44. data/lib/webpacker/webpack_runner.rb +13 -2
  45. data/package.json +31 -27
  46. data/package/__tests__/config.js +25 -3
  47. data/package/config.js +15 -7
  48. data/package/environments/__tests__/base.js +5 -3
  49. data/package/environments/base.js +41 -4
  50. data/package/environments/production.js +17 -6
  51. data/package/rules/babel.js +10 -4
  52. data/package/rules/file.js +2 -2
  53. data/package/rules/index.js +7 -2
  54. data/package/rules/node_modules.js +22 -0
  55. data/package/utils/__tests__/get_style_rule.js +20 -0
  56. data/package/utils/get_style_rule.js +6 -5
  57. data/package/utils/helpers.js +12 -1
  58. data/test/compiler_test.rb +2 -0
  59. data/test/dev_server_runner_test.rb +51 -0
  60. data/test/helper_test.rb +28 -5
  61. data/test/manifest_test.rb +10 -0
  62. data/test/rake_tasks_test.rb +34 -0
  63. data/test/test_app/config/application.rb +1 -0
  64. data/test/test_app/config/webpack/development.js +0 -0
  65. data/test/test_app/config/webpacker.yml +25 -0
  66. data/test/test_app/package.json +13 -0
  67. data/test/test_app/public/packs/manifest.json +10 -1
  68. data/test/test_app/yarn.lock +11 -0
  69. data/test/webpack_runner_test.rb +51 -0
  70. data/yarn.lock +2669 -1809
  71. metadata +20 -8
  72. data/lib/install/config/.babelrc +0 -41
  73. data/lib/install/config/.postcssrc.yml +0 -3
  74. data/lib/install/examples/react/.babelrc +0 -47
@@ -1,4 +1,4 @@
1
1
  module Webpacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "4.0.0.pre.3".freeze
3
+ VERSION = "4.0.0.rc.1".freeze
4
4
  end
@@ -5,11 +5,22 @@ module Webpacker
5
5
  class WebpackRunner < Webpacker::Runner
6
6
  def run
7
7
  env = Webpacker::Compiler.env
8
- cmd = [ "#{@node_modules_bin_path}/webpack", "--config", @webpack_config ] + @argv
8
+
9
+ cmd = if node_modules_bin_exist?
10
+ ["#{@node_modules_bin_path}/webpack"]
11
+ else
12
+ ["yarn", "webpack"]
13
+ end
14
+ cmd += ["--config", @webpack_config] + @argv
9
15
 
10
16
  Dir.chdir(@app_path) do
11
- exec env, *cmd
17
+ Kernel.exec env, *cmd
12
18
  end
13
19
  end
20
+
21
+ private
22
+ def node_modules_bin_exist?
23
+ File.exist?("#{@node_modules_bin_path}/webpack")
24
+ end
14
25
  end
15
26
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rails/webpacker",
3
- "version": "4.0.0-pre.3",
3
+ "version": "4.0.0-rc.1",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "main": "package/index.js",
6
6
  "files": [
@@ -8,50 +8,54 @@
8
8
  "lib/install/config/webpacker.yml"
9
9
  ],
10
10
  "engines": {
11
- "node": ">=6.14.0",
11
+ "node": ">=6.14.4",
12
12
  "yarn": ">=1.0.0"
13
13
  },
14
14
  "dependencies": {
15
- "@babel/core": "^7.0.0",
16
- "@babel/plugin-proposal-class-properties": "^7.0.0",
17
- "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
18
- "@babel/plugin-syntax-dynamic-import": "^7.0.0",
19
- "@babel/plugin-transform-destructuring": "^7.0.0",
15
+ "@babel/core": "^7.2.0",
16
+ "@babel/plugin-proposal-class-properties": "^7.2.1",
17
+ "@babel/plugin-proposal-object-rest-spread": "^7.2.0",
18
+ "@babel/plugin-syntax-dynamic-import": "^7.2.0",
19
+ "@babel/plugin-transform-destructuring": "^7.2.0",
20
20
  "@babel/plugin-transform-regenerator": "^7.0.0",
21
- "@babel/plugin-transform-runtime": "^7.0.0",
21
+ "@babel/plugin-transform-runtime": "^7.2.0",
22
22
  "@babel/polyfill": "^7.0.0",
23
- "@babel/preset-env": "^7.0.0",
24
- "@babel/runtime": "^7.0.0",
25
- "babel-core": "^7.0.0-bridge",
26
- "babel-loader": "^8.0.0",
23
+ "@babel/preset-env": "^7.2.0",
24
+ "@babel/runtime": "^7.2.0",
25
+ "babel-loader": "^8.0.4",
26
+ "babel-plugin-dynamic-import-node": "^2.2.0",
27
+ "babel-plugin-macros": "^2.4.2",
27
28
  "case-sensitive-paths-webpack-plugin": "^2.1.2",
28
- "compression-webpack-plugin": "^1.1.12",
29
- "css-loader": "^1.0.0",
29
+ "compression-webpack-plugin": "^2.0.0",
30
+ "css-loader": "^2.0.1",
30
31
  "file-loader": "^2.0.0",
31
32
  "glob": "^7.1.3",
32
33
  "js-yaml": "^3.12.0",
33
- "mini-css-extract-plugin": "^0.4.2",
34
- "node-sass": "^4.9.3",
34
+ "mini-css-extract-plugin": "^0.5.0",
35
+ "node-sass": "^4.11.0",
35
36
  "optimize-css-assets-webpack-plugin": "^5.0.1",
36
37
  "path-complete-extname": "^1.0.0",
37
- "postcss-import": "^12.0.0",
38
+ "pnp-webpack-plugin": "^1.2.1",
39
+ "postcss-flexbugs-fixes": "^4.1.0",
40
+ "postcss-import": "^12.0.1",
38
41
  "postcss-loader": "^3.0.0",
39
- "postcss-preset-env": "^5.3.0",
42
+ "postcss-preset-env": "^6.5.0",
43
+ "postcss-safe-parser": "^4.0.1",
40
44
  "sass-loader": "^7.1.0",
41
- "style-loader": "^0.23.0",
42
- "uglifyjs-webpack-plugin": "^1.3.0",
43
- "webpack": "^4.17.1",
44
- "webpack-assets-manifest": "^3.0.2",
45
- "webpack-cli": "^3.1.0",
46
- "webpack-sources": "^1.2.0"
45
+ "style-loader": "^0.23.1",
46
+ "terser-webpack-plugin": "^1.1.0",
47
+ "webpack": "^4.27.1",
48
+ "webpack-assets-manifest": "^3.1.1",
49
+ "webpack-cli": "^3.1.2",
50
+ "webpack-sources": "^1.3.0"
47
51
  },
48
52
  "devDependencies": {
49
- "eslint": "^5.4.0",
53
+ "eslint": "^5.10.0",
50
54
  "eslint-config-airbnb": "^17.1.0",
51
55
  "eslint-plugin-import": "^2.14.0",
52
- "eslint-plugin-jsx-a11y": "^6.1.1",
56
+ "eslint-plugin-jsx-a11y": "^6.1.2",
53
57
  "eslint-plugin-react": "^7.11.1",
54
- "jest": "^23.5.0"
58
+ "jest": "^23.6.0"
55
59
  },
56
60
  "jest": {
57
61
  "testRegex": "(/__tests__/.*|(\\.|/))\\.jsx?$",
@@ -1,18 +1,17 @@
1
1
  /* global test expect, describe */
2
2
 
3
- const { chdirCwd, chdirTestApp } = require('../utils/helpers')
3
+ const { chdirCwd, chdirTestApp, resetEnv } = require('../utils/helpers')
4
4
 
5
5
  chdirTestApp()
6
6
 
7
7
  const config = require('../config')
8
8
 
9
9
  describe('Config', () => {
10
- beforeEach(() => jest.resetModules())
10
+ beforeEach(() => jest.resetModules() && resetEnv())
11
11
  afterAll(chdirCwd)
12
12
 
13
13
  test('public path', () => {
14
14
  process.env.RAILS_ENV = 'development'
15
- delete process.env.RAILS_RELATIVE_URL_ROOT
16
15
  const config = require('../config')
17
16
  expect(config.publicPath).toEqual('/packs/')
18
17
  })
@@ -25,8 +24,31 @@ describe('Config', () => {
25
24
  expect(config.publicPath).toEqual('/foo/packs/')
26
25
  })
27
26
 
27
+ test('public path with relative root without slash', () => {
28
+ process.env.RAILS_ENV = 'development'
29
+ process.env.RAILS_RELATIVE_URL_ROOT = 'foo'
30
+ const config = require('../config')
31
+ expect(config.publicPath).toEqual('/foo/packs/')
32
+ })
33
+
34
+ test('public path with asset host and relative root', () => {
35
+ process.env.RAILS_ENV = 'development'
36
+ process.env.RAILS_RELATIVE_URL_ROOT = '/foo/'
37
+ process.env.WEBPACKER_ASSET_HOST = 'http://foo.com/'
38
+ const config = require('../config')
39
+ expect(config.publicPath).toEqual('http://foo.com/foo/packs/')
40
+ })
41
+
42
+ test('public path with asset host', () => {
43
+ process.env.RAILS_ENV = 'development'
44
+ process.env.WEBPACKER_ASSET_HOST = 'http://foo.com/'
45
+ const config = require('../config')
46
+ expect(config.publicPath).toEqual('http://foo.com/packs/')
47
+ })
48
+
28
49
  test('should return extensions as listed in app config', () => {
29
50
  expect(config.extensions).toEqual([
51
+ '.mjs',
30
52
  '.js',
31
53
  '.sass',
32
54
  '.scss',
@@ -2,7 +2,7 @@ const { resolve } = require('path')
2
2
  const { safeLoad } = require('js-yaml')
3
3
  const { readFileSync } = require('fs')
4
4
  const deepMerge = require('./utils/deep_merge')
5
- const { isArray } = require('./utils/helpers')
5
+ const { isArray, ensureTrailingSlash } = require('./utils/helpers')
6
6
  const { railsEnv } = require('./env')
7
7
 
8
8
  const defaultConfigPath = require.resolve('../lib/install/config/webpacker.yml')
@@ -21,13 +21,21 @@ if (isArray(app.extensions) && app.extensions.length) delete defaults.extensions
21
21
  const config = deepMerge(defaults, app)
22
22
  config.outputPath = resolve('public', config.public_output_path)
23
23
 
24
- let publicPath = `/${config.public_output_path}/`
25
- // Add prefix to publicPath.
26
- if (process.env.RAILS_RELATIVE_URL_ROOT) {
27
- publicPath = `/${process.env.RAILS_RELATIVE_URL_ROOT}${publicPath}`
24
+ // Ensure that the publicPath includes our asset host so dynamic imports
25
+ // (code-splitting chunks and static assets) load from the CDN instead of a relative path.
26
+ const getPublicPath = () => {
27
+ const rootUrl = process.env.WEBPACKER_ASSET_HOST || '/'
28
+ let packPath = `${config.public_output_path}/`
29
+ // Add relative root prefix to pack path.
30
+ if (process.env.RAILS_RELATIVE_URL_ROOT) {
31
+ let relativeRoot = process.env.RAILS_RELATIVE_URL_ROOT
32
+ relativeRoot = relativeRoot.startsWith('/') ? relativeRoot.substr(1) : relativeRoot
33
+ packPath = `${ensureTrailingSlash(relativeRoot)}${packPath}`
34
+ }
35
+
36
+ return ensureTrailingSlash(rootUrl) + packPath
28
37
  }
29
38
 
30
- // Remove extra slashes.
31
- config.publicPath = publicPath.replace(/(^\/|[^:]\/)\/+/g, '$1')
39
+ config.publicPath = getPublicPath()
32
40
 
33
41
  module.exports = config
@@ -24,7 +24,9 @@ describe('Environment', () => {
24
24
 
25
25
  test('should return entry', () => {
26
26
  const config = environment.toWebpackConfig()
27
- expect(config.entry.application).toEqual(resolve('app', 'javascript', 'packs', 'application.js'))
27
+ expect(config.entry.application).toEqual(
28
+ resolve('app', 'javascript', 'packs', 'application.js')
29
+ )
28
30
  })
29
31
 
30
32
  test('should return output', () => {
@@ -38,8 +40,8 @@ describe('Environment', () => {
38
40
  const defaultRules = Object.keys(rules)
39
41
  const configRules = config.module.rules
40
42
 
41
- expect(defaultRules.length).toBeGreaterThan(1)
42
- expect(configRules.length).toEqual(defaultRules.length)
43
+ expect(defaultRules.length).toEqual(7)
44
+ expect(configRules.length).toEqual(8)
43
45
  })
44
46
 
45
47
  test('should return default plugins', () => {
@@ -11,6 +11,10 @@ const webpack = require('webpack')
11
11
  const MiniCssExtractPlugin = require('mini-css-extract-plugin')
12
12
  const WebpackAssetsManifest = require('webpack-assets-manifest')
13
13
  const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
14
+ const PnpWebpackPlugin = require('pnp-webpack-plugin')
15
+
16
+ const { isNotObject, prettyPrint } = require('../utils/helpers')
17
+ const deepMerge = require('../utils/deep_merge')
14
18
 
15
19
  const { ConfigList, ConfigObject } = require('../config_types')
16
20
  const rules = require('../rules')
@@ -30,7 +34,7 @@ const getPluginList = () => {
30
34
  )
31
35
  result.append('CaseSensitivePaths', new CaseSensitivePathsPlugin())
32
36
  result.append(
33
- 'ExtractText',
37
+ 'MiniCssExtract',
34
38
  new MiniCssExtractPlugin({
35
39
  filename: '[name]-[contenthash:8].css',
36
40
  chunkFilename: '[name]-[contenthash:8].chunk.css'
@@ -39,6 +43,8 @@ const getPluginList = () => {
39
43
  result.append(
40
44
  'Manifest',
41
45
  new WebpackAssetsManifest({
46
+ integrity: true,
47
+ entrypoints: true,
42
48
  writeToDisk: true,
43
49
  publicPath: true
44
50
  })
@@ -85,11 +91,13 @@ const getBaseConfig = () => new ConfigObject({
85
91
  },
86
92
 
87
93
  resolve: {
88
- extensions: config.extensions
94
+ extensions: config.extensions,
95
+ plugins: [PnpWebpackPlugin]
89
96
  },
90
97
 
91
98
  resolveLoader: {
92
- modules: ['node_modules']
99
+ modules: ['node_modules'],
100
+ plugins: [PnpWebpackPlugin.moduleLoader(module)]
93
101
  },
94
102
 
95
103
  node: {
@@ -110,13 +118,42 @@ module.exports = class Base {
110
118
  this.resolvedModules = getModulePaths()
111
119
  }
112
120
 
121
+ splitChunks(callback = null) {
122
+ let appConfig = {}
123
+ const defaultConfig = {
124
+ optimization: {
125
+ // Split vendor and common chunks
126
+ // https://twitter.com/wSokra/status/969633336732905474
127
+ splitChunks: {
128
+ chunks: 'all',
129
+ name: false
130
+ },
131
+ // Separate runtime chunk to enable long term caching
132
+ // https://twitter.com/wSokra/status/969679223278505985
133
+ runtimeChunk: true
134
+ }
135
+ }
136
+
137
+ if (callback) {
138
+ appConfig = callback(defaultConfig)
139
+ if (isNotObject(appConfig)) {
140
+ throw new Error(`
141
+ ${prettyPrint(appConfig)} is not a valid splitChunks configuration.
142
+ See https://webpack.js.org/plugins/split-chunks-plugin/#configuration
143
+ `)
144
+ }
145
+ }
146
+
147
+ return this.config.merge(deepMerge(defaultConfig, appConfig))
148
+ }
149
+
113
150
  toWebpackConfig() {
114
151
  return this.config.merge({
115
152
  entry: this.entry.toObject(),
116
153
 
117
154
  module: {
118
155
  strictExportPresence: true,
119
- rules: this.loaders.values()
156
+ rules: [{ parser: { requireEnsure: false } }, ...this.loaders.values()]
120
157
  },
121
158
 
122
159
  plugins: this.plugins.values(),
@@ -1,6 +1,7 @@
1
- const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
1
+ const TerserPlugin = require('terser-webpack-plugin')
2
2
  const CompressionPlugin = require('compression-webpack-plugin')
3
3
  const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
4
+ const safePostCssParser = require('postcss-safe-parser')
4
5
  const Base = require('./base')
5
6
 
6
7
  module.exports = class extends Base {
@@ -10,13 +11,23 @@ module.exports = class extends Base {
10
11
  this.plugins.append(
11
12
  'Compression',
12
13
  new CompressionPlugin({
13
- asset: '[path].gz[query]',
14
+ filename: '[path].gz[query]',
14
15
  algorithm: 'gzip',
16
+ cache: true,
15
17
  test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
16
18
  })
17
19
  )
18
20
 
19
- this.plugins.append('OptimizeCSSAssets', new OptimizeCSSAssetsPlugin())
21
+ this.plugins.append(
22
+ 'OptimizeCSSAssets',
23
+ new OptimizeCSSAssetsPlugin({
24
+ parser: safePostCssParser,
25
+ map: {
26
+ inline: false,
27
+ annotation: true
28
+ }
29
+ })
30
+ )
20
31
 
21
32
  this.config.merge({
22
33
  devtool: 'nosources-source-map',
@@ -24,13 +35,13 @@ module.exports = class extends Base {
24
35
  bail: true,
25
36
  optimization: {
26
37
  minimizer: [
27
- new UglifyJsPlugin({
38
+ new TerserPlugin({
28
39
  parallel: true,
29
40
  cache: true,
30
41
  sourceMap: true,
31
- uglifyOptions: {
42
+ terserOptions: {
32
43
  parse: {
33
- // Let uglify-js parse ecma 8 code but always output
44
+ // Let terser parse ecma 8 code but always output
34
45
  // ES5 compliant code for older browsers
35
46
  ecma: 8
36
47
  },
@@ -1,14 +1,20 @@
1
- const { join } = require('path')
2
- const { cache_path: cachePath } = require('../config')
1
+ const { join, resolve } = require('path')
2
+ const { cache_path: cachePath, source_path: sourcePath } = require('../config')
3
+ const { nodeEnv } = require('../env')
3
4
 
5
+ // Process application Javascript code with Babel.
6
+ // Uses application .babelrc to apply any transformations
4
7
  module.exports = {
5
- test: /\.(js|jsx)?(\.erb)?$/,
8
+ test: /\.(js|jsx|mjs)?(\.erb)?$/,
9
+ include: resolve(sourcePath),
6
10
  exclude: /node_modules/,
7
11
  use: [
8
12
  {
9
13
  loader: 'babel-loader',
10
14
  options: {
11
- cacheDirectory: join(cachePath, 'babel-loader')
15
+ cacheDirectory: join(cachePath, 'babel-loader-node-modules'),
16
+ cacheCompression: nodeEnv === 'production',
17
+ compact: nodeEnv === 'production'
12
18
  }
13
19
  }
14
20
  ]
@@ -1,8 +1,8 @@
1
1
  const { join } = require('path')
2
- const { source_path: sourcePath } = require('../config')
2
+ const { source_path: sourcePath, static_assets_extensions: fileExtensions } = require('../config')
3
3
 
4
4
  module.exports = {
5
- test: /\.(jpg|jpeg|png|gif|tiff|ico|svg|eot|otf|ttf|woff|woff2)$/i,
5
+ test: new RegExp(`(${fileExtensions.join('|')})$`, 'i'),
6
6
  use: [
7
7
  {
8
8
  loader: 'file-loader',
@@ -4,12 +4,17 @@ const css = require('./css')
4
4
  const sass = require('./sass')
5
5
  const moduleCss = require('./module.css')
6
6
  const moduleSass = require('./module.sass')
7
+ const nodeModules = require('./node_modules')
7
8
 
9
+ // Webpack loaders are processed in reverse order
10
+ // https://webpack.js.org/concepts/loaders/#loader-features
11
+ // Lastly, process static files using file loader
8
12
  module.exports = {
9
- babel,
13
+ file,
10
14
  css,
11
15
  sass,
12
16
  moduleCss,
13
17
  moduleSass,
14
- file
18
+ nodeModules,
19
+ babel
15
20
  }
@@ -0,0 +1,22 @@
1
+ const { join } = require('path')
2
+ const { cache_path: cachePath } = require('../config')
3
+ const { nodeEnv } = require('../env')
4
+
5
+ // Compile standard ES features for JS in node_modules with Babel.
6
+ module.exports = {
7
+ test: /\.(js|mjs)$/,
8
+ exclude: /@babel(?:\/|\\{1,2})runtime/,
9
+ use: [
10
+ {
11
+ loader: 'babel-loader',
12
+ options: {
13
+ babelrc: false,
14
+ presets: [['@babel/preset-env', { modules: false }]],
15
+ cacheDirectory: join(cachePath, 'babel-loader-node-modules'),
16
+ cacheCompression: nodeEnv === 'production',
17
+ compact: false,
18
+ sourceMaps: false
19
+ }
20
+ }
21
+ ]
22
+ }