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

Allow configuration of use of contentHash for development #234

Merged
merged 13 commits into from
Jun 22, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _Please add entries here for your pull requests that are not yet released._
- Set CSS modules mode depending on file type. [PR 261](https://github.com/shakacode/shakapacker/pull/261) by [talyuk](https://github.com/talyuk).
- All standard webpack entries with the camelCase format are now supported in `shakapacker.yml` in snake_case format. [PR276](https://github.com/shakacode/shakapacker/pull/276) by [ahangarha](https://github.com/ahangarha).
- The `shakapacker:install` rake task now has an option to force overriding files using `FORCE=true` environment variable [PR311](https://github.com/shakacode/shakapacker/pull/311) by [ahangarha](https://github.com/ahangarha).
- Allow configuration of use of contentHash for specific environment [PR 234](https://github.com/shakacode/shakapacker/pull/234) by [justin808](https://github/justin808).

### Changed
- Rename Webpacker to Shakapacker in the entire project including config files, binstubs, environment variables, etc. with a high degree of backward compatibility.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ Webpack intelligently includes only necessary files. In this example, the file `

`nested_entries` allows you to have webpack entry points nested in subdirectories. This defaults to false so you don't accidentally create entry points for an entire tree of files. In other words, with `nested_entries: false`, you can have your entire `source_path` used for your source (using the `source_entry_path: /`) and you place files at the top level that you want as entry points. `nested_entries: true` allows you to have entries that are in subdirectories. This is useful if you have entries that are generated, so you can have a `generated` subdirectory and easily separate generated files from the rest of your codebase.

To enable/disable the usage of contentHash in any node environment (specified using the `NODE_ENV` environment variable), add/modify `useContentHash` with a boolean value in `config/shakapacker.yml`. This feature is disabled for all environments except production by default. You may not disable the content hash for a `NODE_ENV` of production as that would break the browser caching of assets. Notice that despite the possibility of enabling this option for the development environment, [it is not recommended](https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling).

#### Setting custom config path

You can use the environment variable `SHAKAPACKER_CONFIG` to enforce a particular path to the config file rather than the default `config/shakapacker.yml`.
Expand Down
9 changes: 9 additions & 0 deletions lib/install/config/shakapacker.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Note: You must restart bin/shakapacker-dev-server for changes to take effect
# This file contains the defaults used by shakapacker.

default: &default
source_path: app/javascript
Expand Down Expand Up @@ -44,6 +45,11 @@ default: &default
# Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
compiler_strategy: digest

# Select whether the compiler will always use a content hash and not just in production
# Don't use contentHash except for production for performance
# https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
useContentHash: false

development:
<<: *default
compile: true
Expand Down Expand Up @@ -105,5 +111,8 @@ production:
# Production depends on precompilation of packs prior to booting for performance.
compile: false

# Use content hash for naming assets. Cannot be overridden by for production.
useContentHash: true

# Cache manifest.json for performance
cache_manifest: true
4 changes: 2 additions & 2 deletions package/environments/__tests__/base-bc.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ describe('Base config', () => {
})

test('should return output', () => {
expect(baseConfig.output.filename).toEqual('js/[name].js')
expect(baseConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(baseConfig.output.chunkFilename).toEqual(
'js/[name].chunk.js'
'js/[name]-[contenthash].chunk.js'
)
})

Expand Down
4 changes: 2 additions & 2 deletions package/environments/__tests__/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ describe('Base config', () => {
})

test('should return output', () => {
expect(baseConfig.output.filename).toEqual('js/[name].js')
expect(baseConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(baseConfig.output.chunkFilename).toEqual(
'js/[name].chunk.js'
'js/[name]-[contenthash].chunk.js'
)
})

Expand Down
53 changes: 53 additions & 0 deletions package/environments/__tests__/development.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* global test expect, describe, afterAll, beforeEach */

const { chdirTestApp, resetEnv } = require('../../utils/helpers')
const rootPath = process.cwd()
chdirTestApp()

describe('Development specific config', () => {
beforeEach(() => {
jest.resetModules()
resetEnv()
process.env['NODE_ENV'] = 'development'
})
afterAll(() => process.chdir(rootPath))

describe('with config.useContentHash = true', () => {
test('sets filename to use contentHash', () => {
const config = require("../../config");
config.useContentHash = true
const environmnetConfig = require('../development')

expect(environmnetConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name]-[contenthash].chunk.js'
)
})
})

describe('with config.useContentHash = false', () => {
test('sets filename without using contentHash', () => {
const config = require("../../config");
config.useContentHash = false
const environmnetConfig = require('../development')

expect(environmnetConfig.output.filename).toEqual('js/[name].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name].chunk.js'
)
})
})

describe('with unset config.useContentHash', () => {
test('sets filename without using contentHash', () => {
const config = require("../../config");
delete config.useContentHash
const environmnetConfig = require('../development')

expect(environmnetConfig.output.filename).toEqual('js/[name].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name].chunk.js'
)
})
})
})
53 changes: 53 additions & 0 deletions package/environments/__tests__/production.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* global test expect, describe, afterAll, beforeEach */

const { chdirTestApp, resetEnv } = require('../../utils/helpers')
const rootPath = process.cwd()
chdirTestApp()

describe('Production specific config', () => {
beforeEach(() => {
jest.resetModules()
resetEnv()
process.env['NODE_ENV'] = 'production'
})
afterAll(() => process.chdir(rootPath))

describe('with config.useContentHash = true', () => {
test('sets filename to use contentHash', () => {
const config = require("../../config");
config.useContentHash = true
const environmnetConfig = require('../production')

expect(environmnetConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name]-[contenthash].chunk.js'
)
})
})

describe('with config.useContentHash = false', () => {
test('sets filename to use contentHash', () => {
const config = require("../../config");
config.useContentHash = false
const environmnetConfig = require('../production')

expect(environmnetConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name]-[contenthash].chunk.js'
)
})
})

describe('with unset config.useContentHash', () => {
test('sets filename to use contentHash', () => {
const config = require("../../config");
delete config.useContentHash
const environmnetConfig = require('../production')

expect(environmnetConfig.output.filename).toEqual('js/[name]-[contenthash].js')
expect(environmnetConfig.output.chunkFilename).toEqual(
'js/[name]-[contenthash].chunk.js'
)
})
})
})
7 changes: 4 additions & 3 deletions package/environments/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const { sync: globSync } = require('glob')
const WebpackAssetsManifest = require('webpack-assets-manifest')
const webpack = require('webpack')
const rules = require('../rules')
const { isProduction } = require('../env')
const config = require('../config')
const { isProduction } = require('../env')
Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

May you elaborate? isProduction checked nodeEnv already and it seems that is how we check the environment in on JS side. Before also we were adding content hash if isProduction was true.

Should I do anything different?

const { moduleExists } = require('../utils/helpers')

const getEntryObject = () => {
Expand Down Expand Up @@ -68,7 +68,7 @@ const getPlugins = () => {
]

if (moduleExists('css-loader') && moduleExists('mini-css-extract-plugin')) {
const hash = isProduction ? '-[contenthash:8]' : ''
const hash = isProduction || config.useContentHash ? '-[contenthash:8]' : ''
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins.push(
new MiniCssExtractPlugin({
Expand All @@ -87,7 +87,8 @@ const getPlugins = () => {

// Don't use contentHash except for production for performance
// https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
const hash = isProduction ? '-[contenthash]' : ''
const hash = isProduction || config.useContentHash ? '-[contenthash]' : ''

module.exports = {
mode: 'production',
output: {
Expand Down
9 changes: 9 additions & 0 deletions package/environments/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const CompressionPlugin = require('compression-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const baseConfig = require('./base')
const { moduleExists } = require('../utils/helpers')
const config = require('../config')

const getPlugins = () => {
const plugins = []
Expand Down Expand Up @@ -76,4 +77,12 @@ const productionConfig = {
}
}

if (config.useContentHash === true) {
// eslint-disable-next-line no-console
console.warn(`⚠️ WARNING
Setting 'useContentHash' to 'false' in the production environment (specified by NODE_ENV environment variable) is not allowed!
Content hashes get added to the filenames regardless of setting useContentHash in 'shakapacker.yml' to false.
`)
}
Comment on lines +80 to +86
Copy link

Choose a reason for hiding this comment

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

I just received this warning when deploying the Shakapacker v7 upgrade to production. Guessing from the warning, I would assume that the condition is incorrect. Shouldn't it state config.useContentHash === false?

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for reporting

@justin808 fixed in #320


module.exports = merge(baseConfig, productionConfig)