webpacker 2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +21 -21
  4. data/CHANGELOG.md +107 -4
  5. data/Gemfile +3 -1
  6. data/Gemfile.lock +15 -8
  7. data/README.md +137 -937
  8. data/docs/assets.md +106 -0
  9. data/docs/css.md +82 -0
  10. data/docs/deployment.md +39 -0
  11. data/docs/env.md +62 -0
  12. data/docs/es6.md +53 -0
  13. data/docs/folder-structure.md +66 -0
  14. data/docs/misc.md +23 -0
  15. data/docs/props.md +105 -0
  16. data/docs/testing.md +45 -0
  17. data/docs/troubleshooting.md +65 -0
  18. data/docs/typescript.md +115 -0
  19. data/docs/webpack-dev-server.md +32 -0
  20. data/docs/webpack.md +108 -0
  21. data/docs/yarn.md +12 -0
  22. data/lib/install/angular.rb +4 -7
  23. data/lib/install/bin/webpack-dev-server.tt +35 -11
  24. data/lib/install/bin/webpack.tt +3 -4
  25. data/lib/install/config/.babelrc +1 -0
  26. data/lib/install/config/.postcssrc.yml +1 -2
  27. data/lib/install/config/webpack/development.js +2 -31
  28. data/lib/install/config/webpack/environment.js +3 -0
  29. data/lib/install/config/webpack/production.js +2 -34
  30. data/lib/install/config/webpack/test.js +2 -5
  31. data/lib/install/config/webpacker.yml +20 -2
  32. data/lib/install/elm.rb +6 -11
  33. data/lib/install/examples/vue/hello_vue.js +31 -2
  34. data/lib/install/react.rb +2 -5
  35. data/lib/install/template.rb +3 -8
  36. data/lib/install/vue.rb +4 -7
  37. data/lib/tasks/webpacker.rake +1 -1
  38. data/lib/tasks/webpacker/{check_webpack_binstubs.rake → check_binstubs.rake} +3 -2
  39. data/lib/tasks/webpacker/check_node.rake +8 -6
  40. data/lib/tasks/webpacker/check_yarn.rake +2 -2
  41. data/lib/tasks/webpacker/clobber.rake +2 -3
  42. data/lib/tasks/webpacker/compile.rake +16 -18
  43. data/lib/tasks/webpacker/verify_install.rake +5 -5
  44. data/lib/tasks/webpacker/yarn_install.rake +1 -1
  45. data/lib/webpacker.rb +15 -11
  46. data/lib/webpacker/commands.rb +22 -0
  47. data/lib/webpacker/compiler.rb +66 -10
  48. data/lib/webpacker/configuration.rb +54 -38
  49. data/lib/webpacker/dev_server.rb +47 -0
  50. data/lib/webpacker/dev_server_proxy.rb +24 -0
  51. data/lib/webpacker/helper.rb +23 -5
  52. data/lib/webpacker/instance.rb +44 -0
  53. data/lib/webpacker/manifest.rb +58 -34
  54. data/lib/webpacker/railtie.rb +22 -3
  55. data/lib/webpacker/version.rb +2 -1
  56. data/package.json +37 -7
  57. data/package/asset_host.js +21 -0
  58. data/package/config.js +8 -0
  59. data/package/environment.js +95 -0
  60. data/package/environments/development.js +47 -0
  61. data/package/environments/production.js +34 -0
  62. data/package/environments/test.js +3 -0
  63. data/package/index.js +16 -0
  64. data/package/loaders/babel.js +11 -0
  65. data/{lib/install/config/loaders/core → package/loaders}/coffee.js +0 -0
  66. data/{lib/install/config/loaders/installers → package/loaders}/elm.js +4 -5
  67. data/{lib/install/config/loaders/core → package/loaders}/erb.js +0 -0
  68. data/package/loaders/file.js +15 -0
  69. data/package/loaders/style.js +31 -0
  70. data/{lib/install/config/loaders/installers/angular.js → package/loaders/typescript.js} +1 -1
  71. data/package/loaders/vue.js +12 -0
  72. data/test/compiler_test.rb +20 -0
  73. data/test/configuration_test.rb +43 -19
  74. data/test/dev_server_test.rb +24 -0
  75. data/test/helper_test.rb +21 -5
  76. data/test/manifest_test.rb +25 -19
  77. data/test/test_app/public/packs/manifest.json +3 -1
  78. data/test/webpacker_test_helper.rb +40 -0
  79. data/webpacker.gemspec +1 -1
  80. data/yarn.lock +4701 -578
  81. metadata +52 -29
  82. data/lib/install/config/loaders/core/assets.js +0 -12
  83. data/lib/install/config/loaders/core/babel.js +0 -5
  84. data/lib/install/config/loaders/core/sass.js +0 -15
  85. data/lib/install/config/loaders/installers/react.js +0 -5
  86. data/lib/install/config/loaders/installers/vue.js +0 -13
  87. data/lib/install/config/webpack/configuration.js +0 -35
  88. data/lib/install/config/webpack/shared.js +0 -58
  89. data/lib/webpacker/env.rb +0 -23
  90. data/lib/webpacker/file_loader.rb +0 -24
  91. data/test/env_test.rb +0 -14
  92. data/test/webpacker_test.rb +0 -15
@@ -1,51 +1,75 @@
1
- # Singleton registry for accessing the packs path using generated manifest.
1
+ # Singleton registry for accessing the packs path using a generated manifest.
2
2
  # This allows javascript_pack_tag, stylesheet_pack_tag, asset_pack_path to take a reference to,
3
3
  # say, "calendar.js" or "calendar.css" and turn it into "/packs/calendar.js" or
4
- # "/packs/calendar.css" in development. In production mode, it returns compiles
5
- # files, # "/packs/calendar-1016838bab065ae1e314.js" and
6
- # "/packs/calendar-1016838bab065ae1e314.css" for long-term caching
4
+ # "/packs/calendar.css" in development.
5
+ #
6
+ # In production mode, it returns compiles files, like
7
+ # "/packs/calendar-1016838bab065ae1e314.js" and "/packs/calendar-1016838bab065ae1e314.css",
8
+ # for long-term caching.
9
+ #
10
+ # When the configuration is set to on-demand compilation, with the `compile: true` option in
11
+ # the webpacker.yml file, any lookups will be preceeded by a compilation if one is needed.
12
+ class Webpacker::Manifest
13
+ class MissingEntryError < StandardError; end
7
14
 
8
- require "webpacker/file_loader"
15
+ delegate :config, :compiler, :dev_server, to: :@webpacker
9
16
 
10
- class Webpacker::Manifest < Webpacker::FileLoader
11
- class << self
12
- def file_path
13
- Webpacker::Configuration.manifest_path
14
- end
17
+ def initialize(webpacker)
18
+ @webpacker = webpacker
19
+ end
15
20
 
16
- def lookup(name)
17
- load if Webpacker.env.development?
21
+ def refresh
22
+ @data = load
23
+ end
18
24
 
19
- if Webpacker.env.test?
20
- find(name) || compile_and_find!(name)
21
- else
22
- find!(name)
23
- end
25
+ def lookup(name)
26
+ compile if compiling?
27
+ find name
28
+ end
29
+
30
+ private
31
+ def compiling?
32
+ config.compile? && !dev_server.running?
24
33
  end
25
34
 
26
- def lookup_path(name)
27
- Rails.root.join(File.join(Webpacker::Configuration.public_path, lookup(name)))
35
+ def compile
36
+ Webpacker.logger.tagged("Webpacker") { compiler.compile }
28
37
  end
29
38
 
30
- private
31
- def find(name)
32
- instance.data[name.to_s] if instance
33
- end
39
+ def find(name)
40
+ data[name.to_s] || handle_missing_entry(name)
41
+ end
34
42
 
35
- def find!(name)
36
- raise Webpacker::FileLoader::FileLoaderError.new("Webpacker::Manifest.load must be called first") unless instance
37
- instance.data[name.to_s] || raise(Webpacker::FileLoader::NotFoundError.new("Can't find #{name} in #{file_path}. Is webpack still compiling?"))
38
- end
43
+ def handle_missing_entry(name)
44
+ raise Webpacker::Manifest::MissingEntryError, missing_file_from_manifest_error(name)
45
+ end
39
46
 
40
- def compile_and_find!(name)
41
- Webpacker.compile
42
- find!(name)
47
+ def missing_file_from_manifest_error(bundle_name)
48
+ msg = <<-MSG
49
+ Webpacker can't find #{bundle_name} in #{config.public_manifest_path}. Possible causes:
50
+ 1. You want to set wepbacker.yml value of compile to true for your environment
51
+ unless you are using the `webpack -w` or the webpack-dev-server.
52
+ 2. Webpack has not yet re-run to reflect updates.
53
+ 3. You have misconfigured Webpacker's config/webpacker.yml file.
54
+ 4. Your Webpack configuration is not creating a manifest.
55
+ Your manifest contains:
56
+ #{JSON.pretty_generate(@data)}
57
+ MSG
58
+ end
59
+
60
+ def data
61
+ if config.cache_manifest?
62
+ @data ||= load
63
+ else
64
+ refresh
43
65
  end
44
- end
66
+ end
45
67
 
46
- private
47
68
  def load
48
- return super unless File.exist?(@path)
49
- JSON.parse(File.read(@path))
69
+ if config.public_manifest_path.exist?
70
+ JSON.parse config.public_manifest_path.read
71
+ else
72
+ {}
73
+ end
50
74
  end
51
75
  end
@@ -1,9 +1,18 @@
1
1
  require "rails/railtie"
2
2
 
3
3
  require "webpacker/helper"
4
+ require "webpacker/dev_server_proxy"
4
5
 
5
6
  class Webpacker::Engine < ::Rails::Engine
6
- initializer :webpacker do |app|
7
+ initializer "webpacker.proxy" do |app|
8
+ if Rails.env.development?
9
+ app.middleware.insert_before 0,
10
+ Rails::VERSION::MAJOR >= 5 ?
11
+ Webpacker::DevServerProxy : "Webpacker::DevServerProxy", ssl_verify_none: true
12
+ end
13
+ end
14
+
15
+ initializer "webpacker.helper" do |app|
7
16
  ActiveSupport.on_load :action_controller do
8
17
  ActionController::Base.helper Webpacker::Helper
9
18
  end
@@ -11,8 +20,18 @@ class Webpacker::Engine < ::Rails::Engine
11
20
  ActiveSupport.on_load :action_view do
12
21
  include Webpacker::Helper
13
22
  end
23
+ end
24
+
25
+ initializer "webpacker.logger" do
26
+ config.after_initialize do |app|
27
+ Webpacker.logger = ::Rails.logger
28
+ end
29
+ end
14
30
 
15
- Webpacker.bootstrap
16
- Spring.after_fork { Webpacker.bootstrap } if defined?(Spring)
31
+ initializer "webpacker.bootstrap" do
32
+ if defined?(Rails::Server) || defined?(Rails::Console)
33
+ Webpacker.bootstrap
34
+ Spring.after_fork { Webpacker.bootstrap } if defined?(Spring)
35
+ end
17
36
  end
18
37
  end
@@ -1,3 +1,4 @@
1
1
  module Webpacker
2
- VERSION = "2.0".freeze
2
+ # Change the version in package.json too, please!
3
+ VERSION = "3.0.0".freeze
3
4
  end
data/package.json CHANGED
@@ -1,12 +1,42 @@
1
1
  {
2
- "name": "webpacker",
3
- "version": "1.0.0",
4
- "description": "Webpacker makes it easy to use the JavaScript preprocessor and bundler [Webpack](https://webpack.github.io) to manage application-like JavaScript in Rails. It coexists with the asset pipeline, as the purpose is only to use Webpack for app-like JavaScript, not images, css, or even JavaScript Sprinkles (that all continues to live in app/assets).",
5
- "main": "index.js",
2
+ "name": "@rails/webpacker",
3
+ "version": "3.0.0",
4
+ "description": "Use Webpack to manage app-like JavaScript modules in Rails",
5
+ "main": "package/index.js",
6
+ "files": [
7
+ "package"
8
+ ],
6
9
  "engines": {
7
- "node": ">= 6.4.0"
10
+ "node": ">= 6.0.0"
11
+ },
12
+ "dependencies": {
13
+ "babel-core": "^6.26.0",
14
+ "babel-loader": "^7.1.2",
15
+ "babel-plugin-syntax-dynamic-import": "^6.18.0",
16
+ "babel-plugin-transform-class-properties": "^6.24.1",
17
+ "babel-plugin-transform-object-rest-spread": "^6.26.0",
18
+ "babel-polyfill": "^6.26.0",
19
+ "babel-preset-env": "^1.6.0",
20
+ "coffee-loader": "^0.8.0",
21
+ "coffee-script": "^1.12.7",
22
+ "compression-webpack-plugin": "^1.0.0",
23
+ "css-loader": "^0.28.5",
24
+ "extract-text-webpack-plugin": "^3.0.0",
25
+ "file-loader": "^0.11.2",
26
+ "glob": "^7.1.2",
27
+ "js-yaml": "^3.9.1",
28
+ "node-sass": "^4.5.3",
29
+ "path-complete-extname": "^0.1.0",
30
+ "postcss-cssnext": "^3.0.2",
31
+ "postcss-loader": "^2.0.6",
32
+ "postcss-smart-import": "^0.7.5",
33
+ "rails-erb-loader": "^5.2.1",
34
+ "resolve-url-loader": "^2.1.0",
35
+ "sass-loader": "^6.0.6",
36
+ "style-loader": "^0.18.2",
37
+ "webpack": "^3.5.5",
38
+ "webpack-manifest-plugin": "^1.3.1"
8
39
  },
9
- "dependencies": {},
10
40
  "devDependencies": {
11
41
  "eslint": "^3.16.1",
12
42
  "eslint-config-airbnb": "^14.1.0",
@@ -16,7 +46,7 @@
16
46
  },
17
47
  "scripts": {
18
48
  "test": "echo \"Error: no test specified\" && exit 1",
19
- "lint": "yarn run eslint lib/"
49
+ "lint": "eslint {package,lib}/"
20
50
  },
21
51
  "repository": {
22
52
  "type": "git",
@@ -0,0 +1,21 @@
1
+ const config = require('./config')
2
+ const { resolve } = require('path')
3
+
4
+ function removeOuterSlashes(string) {
5
+ return string.replace(/^\/*/, '').replace(/\/*$/, '')
6
+ }
7
+
8
+ function formatPublicPath(host = '', path = '') {
9
+ let formattedHost = removeOuterSlashes(host)
10
+ if (formattedHost && !/^http/i.test(formattedHost)) {
11
+ formattedHost = `//${formattedHost}`
12
+ }
13
+ const formattedPath = removeOuterSlashes(path)
14
+ return `${formattedHost}/${formattedPath}/`
15
+ }
16
+
17
+ module.exports = {
18
+ path: resolve('public', config.public_output_path),
19
+ publicPath: `/${config.public_output_path}/`.replace(/([^:]\/)\/+/g, '$1'),
20
+ publicPathWithHost: formatPublicPath(process.env.ASSET_HOST, config.public_output_path)
21
+ }
data/package/config.js ADDED
@@ -0,0 +1,8 @@
1
+ const { resolve } = require('path')
2
+ const { safeLoad } = require('js-yaml')
3
+ const { readFileSync } = require('fs')
4
+
5
+ const filePath = resolve('config', 'webpacker.yml')
6
+ const config = safeLoad(readFileSync(filePath), 'utf8')
7
+
8
+ module.exports = config[process.env.NODE_ENV]
@@ -0,0 +1,95 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ const config = require('./config')
5
+ const assetHost = require('./asset_host')
6
+
7
+ const { basename, dirname, join, relative, resolve } = require('path')
8
+ const { sync } = require('glob')
9
+ const extname = require('path-complete-extname')
10
+
11
+ const webpack = require('webpack')
12
+ const ExtractTextPlugin = require('extract-text-webpack-plugin')
13
+ const ManifestPlugin = require('webpack-manifest-plugin')
14
+
15
+ function getLoaderMap() {
16
+ const result = new Map()
17
+ const paths = sync(resolve(__dirname, 'loaders', '*.js'))
18
+ paths.forEach((path) => {
19
+ const name = basename(path, extname(path))
20
+ result.set(name, require(path))
21
+ })
22
+ return result
23
+ }
24
+
25
+ function getPluginMap() {
26
+ const result = new Map()
27
+ result.set('Environment', new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(process.env))))
28
+ result.set('ExtractText', new ExtractTextPlugin('[name]-[contenthash].css'))
29
+ result.set('Manifest', new ManifestPlugin({ publicPath: assetHost.publicPath, writeToFileEmit: true }))
30
+ return result
31
+ }
32
+
33
+ function getExtensionsGlob() {
34
+ const { extensions } = config
35
+ if (!extensions.length) {
36
+ throw new Error('You must configure at least one extension to compile in webpacker.yml')
37
+ }
38
+ return extensions.length === 1 ? `**/${extensions[0]}` : `**/*{${extensions.join(',')}}`
39
+ }
40
+
41
+ function getEntryObject() {
42
+ const result = {}
43
+ const glob = getExtensionsGlob()
44
+ const rootPath = join(config.source_path, config.source_entry_path)
45
+ const paths = sync(join(rootPath, glob))
46
+ paths.forEach((path) => {
47
+ const namespace = relative(join(rootPath), dirname(path))
48
+ const name = join(namespace, basename(path, extname(path)))
49
+ result[name] = resolve(path)
50
+ })
51
+ return result
52
+ }
53
+
54
+ function getModulePaths() {
55
+ let result = [resolve(config.source_path), 'node_modules']
56
+ if (config.resolved_paths) {
57
+ result = result.concat(config.resolved_paths)
58
+ }
59
+ return result
60
+ }
61
+
62
+ module.exports = class Environment {
63
+ constructor() {
64
+ this.loaders = getLoaderMap()
65
+ this.plugins = getPluginMap()
66
+ }
67
+
68
+ toWebpackConfig() {
69
+ return {
70
+ entry: getEntryObject(),
71
+
72
+ output: {
73
+ filename: '[name]-[chunkhash].js',
74
+ chunkFilename: '[name]-[chunkhash].chunk.js',
75
+ path: assetHost.path,
76
+ publicPath: assetHost.publicPath
77
+ },
78
+
79
+ module: {
80
+ rules: Array.from(this.loaders.values())
81
+ },
82
+
83
+ plugins: Array.from(this.plugins.values()),
84
+
85
+ resolve: {
86
+ extensions: config.extensions,
87
+ modules: getModulePaths()
88
+ },
89
+
90
+ resolveLoader: {
91
+ modules: ['node_modules']
92
+ }
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,47 @@
1
+ const Environment = require('../environment')
2
+ const { dev_server } = require('../config')
3
+ const assetHost = require('../asset_host')
4
+ const webpack = require('webpack')
5
+
6
+ module.exports = class extends Environment {
7
+ constructor() {
8
+ super()
9
+
10
+ if (dev_server.hmr) {
11
+ this.plugins.set('HotModuleReplacement', new webpack.HotModuleReplacementPlugin())
12
+ this.plugins.set('NamedModules', new webpack.NamedModulesPlugin())
13
+ }
14
+ }
15
+
16
+ toWebpackConfig() {
17
+ const result = super.toWebpackConfig()
18
+ if (dev_server.hmr) {
19
+ result.output.filename = '[name]-[hash].js'
20
+ }
21
+ result.output.pathinfo = true
22
+ result.devtool = 'cheap-eval-source-map'
23
+ result.devServer = {
24
+ host: dev_server.host,
25
+ port: dev_server.port,
26
+ https: dev_server.https,
27
+ hot: dev_server.hmr,
28
+ contentBase: assetHost.path,
29
+ publicPath: assetHost.publicPath,
30
+ clientLogLevel: 'none',
31
+ compress: true,
32
+ historyApiFallback: true,
33
+ headers: {
34
+ 'Access-Control-Allow-Origin': '*'
35
+ },
36
+ overlay: true,
37
+ watchContentBase: true,
38
+ watchOptions: {
39
+ ignored: /node_modules/
40
+ },
41
+ stats: {
42
+ errorDetails: true
43
+ }
44
+ }
45
+ return result
46
+ }
47
+ }
@@ -0,0 +1,34 @@
1
+ const Environment = require('../environment')
2
+ const webpack = require('webpack')
3
+ const CompressionPlugin = require('compression-webpack-plugin')
4
+
5
+ module.exports = class extends Environment {
6
+ constructor() {
7
+ super()
8
+
9
+ this.plugins.set('ModuleConcatenation', new webpack.optimize.ModuleConcatenationPlugin())
10
+
11
+ this.plugins.set('UglifyJs', new webpack.optimize.UglifyJsPlugin({
12
+ sourceMap: true,
13
+ compress: {
14
+ warnings: false
15
+ },
16
+ output: {
17
+ comments: false
18
+ }
19
+ }))
20
+
21
+ this.plugins.set('Compression', new CompressionPlugin({
22
+ asset: '[path].gz[query]',
23
+ algorithm: 'gzip',
24
+ test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
25
+ }))
26
+ }
27
+
28
+ toWebpackConfig() {
29
+ const result = super.toWebpackConfig()
30
+ result.devtool = 'source-map'
31
+ result.stats = 'normal'
32
+ return result
33
+ }
34
+ }
@@ -0,0 +1,3 @@
1
+ const Environment = require('../environment')
2
+
3
+ module.exports = class extends Environment {}
data/package/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ const Environment = require('./environment')
5
+ const { resolve } = require('path')
6
+ const { existsSync } = require('fs')
7
+
8
+ function createEnvironment() {
9
+ const path = resolve(__dirname, 'environments', `${process.env.NODE_ENV}.js`)
10
+ const constructor = existsSync(path) ? require(path) : Environment
11
+ return new constructor()
12
+ }
13
+
14
+ const environment = createEnvironment()
15
+
16
+ module.exports = { environment, Environment }