Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stable webpacker 4.0 release #1823

Merged
merged 15 commits into from
Dec 14, 2018
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
**Please note that Webpacker 3.1.0 and 3.1.1 have some serious bugs so please consider using either 3.0.2 or 3.2.0**

## [Unreleased] - xxxxxx

## Breaking changes

- Order of rules changed so you might have to change append to prepend,
depending on how you want to process packs [#1823](https://github.com/rails/webpacker/pull/1823)
```js
environment.loaders.prepend()
```
- Separate rule to compile node modules
(fixes cases where ES6 libraries were included in the app code) [#1823](https://github.com/rails/webpacker/pull/1823)
- File loader extensions API [#1823](https://github.com/rails/webpacker/pull/1823)
```yml
# webpacker.yml
static_assets_extensions:
- .pdf
# etc..
```

### Added

- Move `.babelrc` and `.postcssrc` to `.js` variant [#1822](https://github.com/rails/webpacker/pull/1822)
- Use postcss safe parser when optimising css assets [#1822](https://github.com/rails/webpacker/pull/1822)
- Add split chunks api (undocumented)
```js
const { environment } = require('@rails/webpacker')
// Enable with default config
environment.splitChunks()
// Configure via a callback
environment.splitChunks((config) => Object.assign({}, config, { optimization: { splitChunks: false }}))
```
- Allow changing static file extensions using webpacker.yml (undocumented)

## [4.0.0-pre.3] - 2018-10-01

### Added
Expand Down
4 changes: 2 additions & 2 deletions lib/install/coffee.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
after: "require('@rails/webpacker')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.loaders.append('coffee', coffee)\n",
"environment.loaders.prepend('coffee', coffee)\n",
before: "module.exports"

say "Updating webpack paths to include .coffee file extension"
insert_into_file Webpacker.config.config_path, "- .coffee\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .coffee\n".indent(4), after: /\s+extensions:\n/

say "Copying the example entry file to #{Webpacker.config.source_entry_path}"
copy_file "#{__dir__}/examples/coffee/hello_coffee.coffee",
Expand Down
15 changes: 15 additions & 0 deletions lib/install/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,22 @@ default: &default
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false

static_assets_extensions:
- .jpg
- .jpeg
- .png
- .gif
- .tiff
- .ico
- .svg
- .eot
- .otf
- .ttf
- .woff
- .woff2

extensions:
- .mjs
- .js
- .sass
- .scss
Expand Down
4 changes: 2 additions & 2 deletions lib/install/elm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
after: "require('@rails/webpacker')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.loaders.append('elm', elm)\n",
"environment.loaders.prepend('elm', elm)\n",
before: "module.exports"

say "Copying Elm example entry file to #{Webpacker.config.source_entry_path}"
Expand All @@ -27,7 +27,7 @@
run "yarn run elm make"

say "Updating webpack paths to include .elm file extension"
insert_into_file Webpacker.config.config_path, "- .elm\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .elm\n".indent(4), after: /\s+extensions:\n/

say "Updating Elm source location"
gsub_file "elm.json", /\"\src\"\n/,
Expand Down
4 changes: 2 additions & 2 deletions lib/install/erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
after: "require('@rails/webpacker')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.loaders.append('erb', erb)\n",
"environment.loaders.prepend('erb', erb)\n",
before: "module.exports"

say "Updating webpack paths to include .erb file extension"
insert_into_file Webpacker.config.config_path, "- .erb\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .erb\n".indent(4), after: /\s+extensions:\n/

say "Copying the example entry file to #{Webpacker.config.source_entry_path}"
copy_file "#{__dir__}/examples/erb/hello_erb.js.erb",
Expand Down
11 changes: 8 additions & 3 deletions lib/install/loaders/typescript.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const PnpWebpackPlugin = require('pnp-webpack-plugin')

module.exports = {
test: /\.(ts|tsx)?(\.erb)?$/,
use: [{
loader: 'ts-loader'
}]
use: [
{
loader: 'ts-loader',
options: PnpWebpackPlugin.tsLoaderOptions()
}
]
}
2 changes: 1 addition & 1 deletion lib/install/react.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
copy_file "#{__dir__}/examples/react/hello_react.jsx", "#{Webpacker.config.source_entry_path}/hello_react.jsx"

say "Updating webpack paths to include .jsx file extension"
insert_into_file Webpacker.config.config_path, "- .jsx\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .jsx\n".indent(4), after: /\s+extensions:\n/

say "Installing all react dependencies"
run "yarn add react react-dom @babel/preset-react prop-types babel-plugin-transform-react-remove-prop-types"
Expand Down
6 changes: 3 additions & 3 deletions lib/install/typescript.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@
after: "require('@rails/webpacker')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.loaders.append('typescript', typescript)\n",
"environment.loaders.prepend('typescript', typescript)\n",
before: "module.exports"

say "Copying tsconfig.json to the Rails root directory for typescript"
copy_file "#{__dir__}/examples/#{example_source}/tsconfig.json", "tsconfig.json"

say "Updating webpack paths to include .ts file extension"
insert_into_file Webpacker.config.config_path, "- .ts\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .ts\n".indent(4), after: /\s+extensions:\n/

say "Updating webpack paths to include .tsx file extension"
insert_into_file Webpacker.config.config_path, "- .tsx\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .tsx\n".indent(4), after: /\s+extensions:\n/

say "Copying the example entry file to #{Webpacker.config.source_entry_path}"
copy_file "#{__dir__}/examples/typescript/hello_typescript.ts",
Expand Down
6 changes: 3 additions & 3 deletions lib/install/vue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
after: "require('@rails/webpacker')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.plugins.append('VueLoaderPlugin', new VueLoaderPlugin())\n",
"environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())\n",
before: "module.exports"

say "Adding vue loader to config/webpack/environment.js"
Expand All @@ -18,11 +18,11 @@
after: "require('vue-loader')\n"

insert_into_file Rails.root.join("config/webpack/environment.js").to_s,
"environment.loaders.append('vue', vue)\n",
"environment.loaders.prepend('vue', vue)\n",
before: "module.exports"

say "Updating webpack paths to include .vue file extension"
insert_into_file Webpacker.config.config_path, "- .vue\n".indent(4), after: /extensions:\n/
insert_into_file Webpacker.config.config_path, "- .vue\n".indent(4), after: /\s+extensions:\n/

say "Copying the example entry file to #{Webpacker.config.source_entry_path}"
copy_file "#{__dir__}/examples/vue/hello_vue.js",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"node-sass": "^4.10.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"path-complete-extname": "^1.0.0",
"pnp-webpack-plugin": "^1.2.1",
"postcss-flexbugs-fixes": "^4.1.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
Expand Down
28 changes: 25 additions & 3 deletions package/__tests__/config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/* global test expect, describe */

const { chdirCwd, chdirTestApp } = require('../utils/helpers')
const { chdirCwd, chdirTestApp, resetEnv } = require('../utils/helpers')

chdirTestApp()

const config = require('../config')

describe('Config', () => {
beforeEach(() => jest.resetModules())
beforeEach(() => jest.resetModules() && resetEnv())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resetEnv like this might give you more problems than you would expect. I would do:

beforeEach(() => {
  jest.resetModules()
  process.env = {}
  // OR
  // delete process.env.RAILS_ENV
})

afterAll(chdirCwd)

test('public path', () => {
process.env.RAILS_ENV = 'development'
delete process.env.RAILS_RELATIVE_URL_ROOT
const config = require('../config')
expect(config.publicPath).toEqual('/packs/')
})
Expand All @@ -25,8 +24,31 @@ describe('Config', () => {
expect(config.publicPath).toEqual('/foo/packs/')
})

test('public path with relative root without slash', () => {
process.env.RAILS_ENV = 'development'
process.env.RAILS_RELATIVE_URL_ROOT = 'foo'
const config = require('../config')
expect(config.publicPath).toEqual('/foo/packs/')
})

test('public path with asset host and relative root', () => {
process.env.RAILS_ENV = 'development'
process.env.RAILS_RELATIVE_URL_ROOT = '/foo/'
process.env.WEBPACKER_ASSET_HOST = 'http://foo.com/'
const config = require('../config')
expect(config.publicPath).toEqual('http://foo.com/foo/packs/')
})

test('public path with asset host', () => {
process.env.RAILS_ENV = 'development'
process.env.WEBPACKER_ASSET_HOST = 'http://foo.com/'
const config = require('../config')
expect(config.publicPath).toEqual('http://foo.com/packs/')
})

test('should return extensions as listed in app config', () => {
expect(config.extensions).toEqual([
'.mjs',
'.js',
'.sass',
'.scss',
Expand Down
22 changes: 15 additions & 7 deletions package/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { resolve } = require('path')
const { safeLoad } = require('js-yaml')
const { readFileSync } = require('fs')
const deepMerge = require('./utils/deep_merge')
const { isArray } = require('./utils/helpers')
const { isArray, ensureTrailingSlash } = require('./utils/helpers')
const { railsEnv } = require('./env')

const defaultConfigPath = require.resolve('../lib/install/config/webpacker.yml')
Expand All @@ -21,13 +21,21 @@ if (isArray(app.extensions) && app.extensions.length) delete defaults.extensions
const config = deepMerge(defaults, app)
config.outputPath = resolve('public', config.public_output_path)

let publicPath = `/${config.public_output_path}/`
// Add prefix to publicPath.
if (process.env.RAILS_RELATIVE_URL_ROOT) {
publicPath = `/${process.env.RAILS_RELATIVE_URL_ROOT}${publicPath}`
// Ensure that the publicPath includes our asset host so dynamic imports
// (code-splitting chunks and static assets) load from the CDN instead of a relative path.
const getPublicPath = () => {
const rootUrl = process.env.WEBPACKER_ASSET_HOST || '/'
let packPath = `${config.public_output_path}/`
// Add relative root prefix to pack path.
if (process.env.RAILS_RELATIVE_URL_ROOT) {
let relativeRoot = process.env.RAILS_RELATIVE_URL_ROOT
relativeRoot = relativeRoot.startsWith('/') ? relativeRoot.substr(1) : relativeRoot
packPath = `${ensureTrailingSlash(relativeRoot)}${packPath}`
}

return ensureTrailingSlash(rootUrl) + packPath
}

// Remove extra slashes.
config.publicPath = publicPath.replace(/(^\/|[^:]\/)\/+/g, '$1')
config.publicPath = getPublicPath()

module.exports = config
8 changes: 5 additions & 3 deletions package/environments/__tests__/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ describe('Environment', () => {

test('should return entry', () => {
const config = environment.toWebpackConfig()
expect(config.entry.application).toEqual(resolve('app', 'javascript', 'packs', 'application.js'))
expect(config.entry.application).toEqual(
resolve('app', 'javascript', 'packs', 'application.js')
)
})

test('should return output', () => {
Expand All @@ -38,8 +40,8 @@ describe('Environment', () => {
const defaultRules = Object.keys(rules)
const configRules = config.module.rules

expect(defaultRules.length).toBeGreaterThan(1)
expect(configRules.length).toEqual(defaultRules.length)
expect(defaultRules.length).toEqual(7)
expect(configRules.length).toEqual(8)
})

test('should return default plugins', () => {
Expand Down
45 changes: 41 additions & 4 deletions package/environments/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const WebpackAssetsManifest = require('webpack-assets-manifest')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const PnpWebpackPlugin = require('pnp-webpack-plugin')

const { isNotObject, prettyPrint } = require('../utils/helpers')
const deepMerge = require('../utils/deep_merge')

const { ConfigList, ConfigObject } = require('../config_types')
const rules = require('../rules')
Expand All @@ -30,7 +34,7 @@ const getPluginList = () => {
)
result.append('CaseSensitivePaths', new CaseSensitivePathsPlugin())
result.append(
'ExtractText',
'MiniCssExtract',
new MiniCssExtractPlugin({
filename: '[name]-[contenthash:8].css',
chunkFilename: '[name]-[contenthash:8].chunk.css'
Expand All @@ -39,6 +43,8 @@ const getPluginList = () => {
result.append(
'Manifest',
new WebpackAssetsManifest({
integrity: true,
entrypoints: true,
writeToDisk: true,
publicPath: true
})
Expand Down Expand Up @@ -85,11 +91,13 @@ const getBaseConfig = () => new ConfigObject({
},

resolve: {
extensions: config.extensions
extensions: config.extensions,
plugins: [PnpWebpackPlugin]
},

resolveLoader: {
modules: ['node_modules']
modules: ['node_modules'],
plugins: [PnpWebpackPlugin.moduleLoader(module)]
},

node: {
Expand All @@ -110,13 +118,42 @@ module.exports = class Base {
this.resolvedModules = getModulePaths()
}

splitChunks(callback = null) {
let appConfig = {}
const defaultConfig = {
optimization: {
// Split vendor and common chunks
// https://twitter.com/wSokra/status/969633336732905474
splitChunks: {
chunks: 'all',
name: false
},
// Separate runtime chunk to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: true
}
}

if (callback) {
appConfig = callback(defaultConfig)
if (isNotObject(appConfig)) {
throw new Error(`
${prettyPrint(appConfig)} is not a valid splitChunks configuration.
See https://webpack.js.org/plugins/split-chunks-plugin/#configuration
`)
}
}

return this.config.merge(deepMerge(defaultConfig, appConfig))
}

toWebpackConfig() {
return this.config.merge({
entry: this.entry.toObject(),

module: {
strictExportPresence: true,
rules: this.loaders.values()
rules: [{ parser: { requireEnsure: false } }, ...this.loaders.values()]
},

plugins: this.plugins.values(),
Expand Down
Loading