From 776837c17a9b64eb6acd8b7cf243815abfa4fe59 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Thu, 22 Jun 2023 11:45:12 +0500 Subject: [PATCH] Allow configuration of use of contentHash for development (#234) 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). --- CHANGELOG.md | 1 + README.md | 2 + lib/install/config/shakapacker.yml | 9 ++++ package/environments/__tests__/base-bc.js | 4 +- package/environments/__tests__/base.js | 4 +- package/environments/__tests__/development.js | 53 +++++++++++++++++++ package/environments/__tests__/production.js | 53 +++++++++++++++++++ package/environments/base.js | 7 +-- package/environments/production.js | 9 ++++ 9 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 package/environments/__tests__/development.js create mode 100644 package/environments/__tests__/production.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3647cf614..483253b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index 2dd5694b1..0abbd2f31 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/lib/install/config/shakapacker.yml b/lib/install/config/shakapacker.yml index df5184bf0..738766bd1 100644 --- a/lib/install/config/shakapacker.yml +++ b/lib/install/config/shakapacker.yml @@ -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 @@ -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 @@ -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 diff --git a/package/environments/__tests__/base-bc.js b/package/environments/__tests__/base-bc.js index 22a1eaeab..bfb53f597 100644 --- a/package/environments/__tests__/base-bc.js +++ b/package/environments/__tests__/base-bc.js @@ -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' ) }) diff --git a/package/environments/__tests__/base.js b/package/environments/__tests__/base.js index d30127738..4bd6121e4 100644 --- a/package/environments/__tests__/base.js +++ b/package/environments/__tests__/base.js @@ -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' ) }) diff --git a/package/environments/__tests__/development.js b/package/environments/__tests__/development.js new file mode 100644 index 000000000..8684ee952 --- /dev/null +++ b/package/environments/__tests__/development.js @@ -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' + ) + }) + }) +}) diff --git a/package/environments/__tests__/production.js b/package/environments/__tests__/production.js new file mode 100644 index 000000000..2ba55ca7a --- /dev/null +++ b/package/environments/__tests__/production.js @@ -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' + ) + }) + }) +}) diff --git a/package/environments/base.js b/package/environments/base.js index 6b11ef821..e047f4e19 100644 --- a/package/environments/base.js +++ b/package/environments/base.js @@ -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') const { moduleExists } = require('../utils/helpers') const getEntryObject = () => { @@ -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({ @@ -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: { diff --git a/package/environments/production.js b/package/environments/production.js index 3eff2d76d..955b9c184 100644 --- a/package/environments/production.js +++ b/package/environments/production.js @@ -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 = [] @@ -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. +`) +} + module.exports = merge(baseConfig, productionConfig)