diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 131db1e1..34b84873 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -526,8 +526,6 @@ class WebpackConfig { } createSharedEntry(name, file) { - logger.deprecation('Encore.createSharedEntry() is deprecated and will be removed in a future version, please use Encore.splitEntryChunks() or Encore.addCacheGroup() instead.'); - if (this.shouldSplitEntryChunks) { throw new Error('Using splitEntryChunks() and createSharedEntry() together is not supported. Use one of these strategies only to optimize your build.'); } @@ -537,6 +535,8 @@ class WebpackConfig { throw new Error('createSharedEntry() cannot be called multiple times: you can only create *one* shared entry.'); } + logger.deprecation('Encore.createSharedEntry() is deprecated and will be removed in a future version, please use Encore.splitEntryChunks() or Encore.addCacheGroup() instead.'); + if (Array.isArray(file)) { throw new Error('Argument 2 to createSharedEntry() must be a single string file: not an array of files. Try creating one file that requires/imports all the modules that should be included.'); } diff --git a/lib/logger.js b/lib/logger.js index 14877dfe..533f42c0 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -69,6 +69,23 @@ module.exports = { log(`${chalk.bgYellow.black('DEPRECATION')} ${chalk.yellow(message)}`); }, + /** + * @returns {Array} + */ + getDeprecations() { + return messages.deprecation; + }, + + /** + * @returns {Array} + */ + getWarnings() { + return messages.warning; + }, + + /** + * @returns {Array} + */ getMessages() { return messages; }, diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index 094cb6e7..3c78e4cd 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -15,7 +15,7 @@ const RuntimeConfig = require('../lib/config/RuntimeConfig'); const path = require('path'); const fs = require('fs'); const webpack = require('webpack'); -const logger = require('../lib/logger'); +const loggerAssert = require('./helpers/logger-assert'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -139,11 +139,9 @@ describe('WebpackConfig object', () => { it('You can omit the opening slash, but get a warning', () => { const config = createConfig(); - logger.reset(); - logger.quiet(); config.setPublicPath('foo'); - expect(logger.getMessages().warning).to.have.lengthOf(1); + loggerAssert.assertWarning('The value passed to setPublicPath() should *usually* start with "/" or be a full URL'); }); }); @@ -206,10 +204,8 @@ describe('WebpackConfig object', () => { it('You can use an opening slash, but get a warning', () => { const config = createConfig(); - logger.reset(); - logger.quiet(); config.setManifestKeyPrefix('/foo/'); - expect(logger.getMessages().warning).to.have.lengthOf(1); + loggerAssert.assertWarning('The value passed to setManifestKeyPrefix "/foo/" starts with "/". This is allowed, but since the key prefix does not normally start with a "/"'); }); }); @@ -384,6 +380,7 @@ describe('WebpackConfig object', () => { it('Calling twice throws an error', () => { const config = createConfig(); config.createSharedEntry('vendor', 'jquery'); + loggerAssert.assertDeprecation('Encore.createSharedEntry() is deprecated'); expect(() => { config.createSharedEntry('vendor2', './main'); @@ -593,15 +590,6 @@ describe('WebpackConfig object', () => { }); describe('configureBabel', () => { - beforeEach(() => { - logger.reset(); - logger.quiet(); - }); - - afterEach(() => { - logger.quiet(false); - }); - it('Calling method sets it', () => { const config = createConfig(); const testCallback = () => {}; @@ -619,7 +607,7 @@ describe('WebpackConfig object', () => { it('Calling with "includeNodeModules" option', () => { const config = createConfig(); - config.configureBabel(() => {}, { include_node_modules: ['foo', 'bar'] }); + config.configureBabel(() => {}, { includeNodeModules: ['foo', 'bar'] }); expect(config.babelOptions.exclude).to.be.a('Function'); @@ -668,16 +656,13 @@ describe('WebpackConfig object', () => { config.runtimeConfig.babelRcFileExists = true; config.configureBabel(() => {}); - const warnings = logger.getMessages().warning; - expect(warnings).to.have.lengthOf(1); - expect(warnings[0]).to.contain('your app already provides an external Babel configuration'); + loggerAssert.assertWarning('your app already provides an external Babel configuration'); }); it('Calling with a whitelisted option when .babelrc is present works fine', () => { const config = createConfig(); config.runtimeConfig.babelRcFileExists = true; config.configureBabel(null, { includeNodeModules: ['foo'] }); - expect(logger.getMessages().warning).to.be.empty; }); it('Calling with a non-whitelisted option when .babelrc is present displays a warning', () => { @@ -685,9 +670,7 @@ describe('WebpackConfig object', () => { config.runtimeConfig.babelRcFileExists = true; config.configureBabel(null, { useBuiltIns: 'foo' }); - const warnings = logger.getMessages().warning; - expect(warnings).to.have.lengthOf(1); - expect(warnings[0]).to.contain('your app already provides an external Babel configuration'); + loggerAssert.assertWarning('your app already provides an external Babel configuration'); }); it('Pass invalid config', () => { @@ -716,15 +699,6 @@ describe('WebpackConfig object', () => { }); describe('configureBabelPresetEnv', () => { - beforeEach(() => { - logger.reset(); - logger.quiet(); - }); - - afterEach(() => { - logger.quiet(false); - }); - it('Calling method sets it', () => { const config = createConfig(); const testCallback = () => {}; @@ -1348,6 +1322,7 @@ describe('WebpackConfig object', () => { config.configureLoaderRule('eslint', callback); expect(config.loaderConfigurationCallbacks['eslint']).to.equal(callback); + loggerAssert.assertWarning('Be careful when using Encore.configureLoaderRule'); }); it('Call method with a not supported loader', () => { @@ -1356,6 +1331,7 @@ describe('WebpackConfig object', () => { expect(() => { config.configureLoaderRule('reason'); }).to.throw('Loader "reason" is not configurable. Valid loaders are "javascript", "css", "images", "fonts", "sass", "less", "stylus", "vue", "eslint", "typescript", "handlebars" and the aliases "js", "ts", "scss".'); + loggerAssert.assertWarning('Be careful when using Encore.configureLoaderRule'); }); it('Call method with not a valid callback', () => { @@ -1364,10 +1340,12 @@ describe('WebpackConfig object', () => { expect(() => { config.configureLoaderRule('eslint'); }).to.throw('Argument 2 to configureLoaderRule() must be a callback function.'); + loggerAssert.assertWarning('Be careful when using Encore.configureLoaderRule'); expect(() => { config.configureLoaderRule('eslint', {}); }).to.throw('Argument 2 to configureLoaderRule() must be a callback function.'); + loggerAssert.assertWarning('Be careful when using Encore.configureLoaderRule'); }); }); diff --git a/test/_unsilencedLogsCheck.js b/test/_unsilencedLogsCheck.js new file mode 100644 index 00000000..3af3a247 --- /dev/null +++ b/test/_unsilencedLogsCheck.js @@ -0,0 +1,28 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const logger = require('../lib/logger'); + +beforeEach(function() { + logger.quiet(); +}); + +afterEach(function() { + if (logger.getDeprecations().length > 0) { + this.test.error(new Error(`There were ${logger.getDeprecations().length} unexpected deprecation log messages: \n${logger.getDeprecations().join('\n')}`)); + } + + if (logger.getWarnings().length > 0) { + this.test.error(new Error(`There were ${logger.getWarnings().length} unexpected warning log messages: \n${logger.getWarnings().join('\n')}`)); + } + + logger.reset(); +}); diff --git a/test/config-generator.js b/test/config-generator.js index 3a0d8e7d..85337ce7 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -18,11 +18,11 @@ const ManifestPlugin = require('webpack-manifest-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const webpack = require('webpack'); const path = require('path'); -const logger = require('../lib/logger'); +const loggerAssert = require('./helpers/logger-assert'); const isWindows = (process.platform === 'win32'); -function createConfig(runtimeConfig = null) { +function createConfig(runtimeConfig = null, disableSingleRuntimeChunk = true) { runtimeConfig = runtimeConfig ? runtimeConfig : new RuntimeConfig(); if (null === runtimeConfig.context) { @@ -37,7 +37,12 @@ function createConfig(runtimeConfig = null) { runtimeConfig.babelRcFileExists = false; } - return new WebpackConfig(runtimeConfig); + const config = new WebpackConfig(runtimeConfig); + if (disableSingleRuntimeChunk) { + config.disableSingleRuntimeChunk(); + } + + return config; } function findPlugin(pluginConstructor, plugins) { @@ -167,6 +172,7 @@ describe('The config-generator function', () => { // pretend we're installed to a subdirectory config.setPublicPath('/subdirectory/build'); config.setManifestKeyPrefix('/build'); + loggerAssert.assertWarning('The value passed to setManifestKeyPrefix "/build" starts with "/"'); const actualConfig = configGenerator(config); @@ -372,10 +378,6 @@ describe('The config-generator function', () => { }); it('enableEslintLoader("extends-name")', () => { - before(() => { - logger.reset(); - }); - const config = createConfig(); config.addEntry('main', './main'); config.publicPath = '/'; @@ -384,7 +386,7 @@ describe('The config-generator function', () => { const actualConfig = configGenerator(config); - expect(JSON.stringify(logger.getMessages().deprecation)).to.contain('enableEslintLoader: Extending from a configuration is deprecated, please use a configuration file instead. See https://eslint.org/docs/user-guide/configuring for more information.'); + loggerAssert.assertDeprecation('enableEslintLoader: Extending from a configuration is deprecated, please use a configuration file instead. See https://eslint.org/docs/user-guide/configuring for more information.'); expect(JSON.stringify(actualConfig.module.rules)).to.contain('eslint-loader'); expect(JSON.stringify(actualConfig.module.rules)).to.contain('extends-name'); }); @@ -1068,15 +1070,6 @@ describe('The config-generator function', () => { }); describe('Test shouldUseSingleRuntimeChunk', () => { - before(() => { - logger.reset(); - logger.quiet(); - }); - - after(() => { - logger.quiet(false); - }); - it('Set to true', () => { const config = createConfig(); config.outputPath = '/tmp/public/build'; @@ -1085,7 +1078,6 @@ describe('The config-generator function', () => { const actualConfig = configGenerator(config); expect(actualConfig.optimization.runtimeChunk).to.equal('single'); - expect(logger.getMessages().deprecation).to.be.empty; }); it('Set to false', () => { @@ -1096,28 +1088,27 @@ describe('The config-generator function', () => { const actualConfig = configGenerator(config); expect(actualConfig.optimization.runtimeChunk).to.be.undefined; - expect(logger.getMessages().deprecation).to.be.empty; }); it('Not set + createSharedEntry()', () => { - const config = createConfig(); + const config = createConfig(null, false); config.outputPath = '/tmp/public/build'; config.setPublicPath('/build/'); config.createSharedEntry('foo', 'bar.js'); const actualConfig = configGenerator(config); expect(actualConfig.optimization.runtimeChunk.name).to.equal('manifest'); - expect(JSON.stringify(logger.getMessages().deprecation)).to.contain('the recommended setting is Encore.enableSingleRuntimeChunk()'); + loggerAssert.assertDeprecation('the recommended setting is Encore.enableSingleRuntimeChunk()'); }); it('Not set without createSharedEntry()', () => { - const config = createConfig(); + const config = createConfig(null, false); config.outputPath = '/tmp/public/build'; config.setPublicPath('/build/'); const actualConfig = configGenerator(config); expect(actualConfig.optimization.runtimeChunk).to.be.undefined; - expect(JSON.stringify(logger.getMessages().deprecation)).to.contain('the recommended setting is Encore.enableSingleRuntimeChunk()'); + loggerAssert.assertDeprecation('the recommended setting is Encore.enableSingleRuntimeChunk()'); }); }); @@ -1148,6 +1139,10 @@ describe('The config-generator function', () => { config.enableSingleRuntimeChunk(); }); + afterEach(function() { + loggerAssert.assertWarning('Be careful when using Encore.configureLoaderRule'); + }); + it('configure rule for "javascript"', () => { config.configureLoaderRule('javascript', (loaderRule) => { loaderRule.test = /\.m?js$/; diff --git a/test/config/path-util.js b/test/config/path-util.js index 2113d014..6eeb72f8 100644 --- a/test/config/path-util.js +++ b/test/config/path-util.js @@ -14,6 +14,7 @@ const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const pathUtil = require('../../lib/config/path-util'); const process = require('process'); +const loggerAssert = require('../helpers/logger-assert'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -54,6 +55,8 @@ describe('path-util getContentBase()', () => { const actualContentBase = pathUtil.getContentBase(config); expect(actualContentBase).to.equal(isWindows ? 'C:\\tmp\\public' : '/tmp/public'); + + loggerAssert.assertWarning('The value passed to setManifestKeyPrefix "/build/" starts with "/". This is allowed, but'); }); it('contentBase is calculated correctly with no public path', function() { diff --git a/test/config/validator.js b/test/config/validator.js index 17e2e7c9..9a8d807d 100644 --- a/test/config/validator.js +++ b/test/config/validator.js @@ -13,7 +13,7 @@ const expect = require('chai').expect; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const validator = require('../../lib/config/validator'); -const logger = require('../../lib/logger'); +const loggerAssert = require('../helpers/logger-assert'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -75,12 +75,9 @@ describe('The validator function', () => { config.addEntry('main', './main'); config.runtimeConfig.useDevServer = true; - logger.reset(); - logger.quiet(); validator(config); - expect(logger.getMessages().warning).to.have.lengthOf(1); - expect(logger.getMessages().warning[0]).to.include('Passing an absolute URL to setPublicPath() *and* using the dev-server can cause issues'); + loggerAssert.assertWarning('Passing an absolute URL to setPublicPath() *and* using the dev-server can cause issues'); }); it('warning with createSharedEntry() and core cache group name', () => { @@ -89,12 +86,9 @@ describe('The validator function', () => { config.setPublicPath('/build'); config.createSharedEntry('vendors', './main'); - logger.reset(); - logger.quiet(); validator(config); - expect(logger.getMessages().warning).to.have.lengthOf(1); - expect(logger.getMessages().warning[0]).to.include('Passing "vendors" to createSharedEntry() is not recommended'); + loggerAssert.assertWarning('Passing "vendors" to createSharedEntry() is not recommended'); }); it('warning with addCacheGroup() and core cache group name', () => { @@ -106,12 +100,9 @@ describe('The validator function', () => { test: /[\\/]main/, }); - logger.reset(); - logger.quiet(); validator(config); - expect(logger.getMessages().warning).to.have.lengthOf(1); - expect(logger.getMessages().warning[0]).to.include('Passing "defaultVendors" to addCacheGroup() is not recommended'); + loggerAssert.assertWarning('Passing "defaultVendors" to addCacheGroup() is not recommended'); }); it('warning with addCacheGroup() and a similar createSharedEntry() name', () => { @@ -124,11 +115,8 @@ describe('The validator function', () => { test: /[\\/]main/, }); - logger.reset(); - logger.quiet(); validator(config); - expect(logger.getMessages().warning).to.have.lengthOf(1); - expect(logger.getMessages().warning[0]).to.include('Using the same name when calling createSharedEntry() and addCacheGroup() is not recommended.'); + loggerAssert.assertWarning('Using the same name when calling createSharedEntry() and addCacheGroup() is not recommended.'); }); }); diff --git a/test/helpers/logger-assert.js b/test/helpers/logger-assert.js new file mode 100644 index 00000000..ce456f90 --- /dev/null +++ b/test/helpers/logger-assert.js @@ -0,0 +1,44 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const logger = require('../../lib/logger'); + +function assertWarning(expectedMessage) { + assertLogMessage(logger.getWarnings(), 'warning', expectedMessage); +} + +function assertDeprecation(expectedMessage) { + assertLogMessage(logger.getDeprecations(), 'deprecation', expectedMessage); +} + +function assertLogMessage(messages, description, expectedMessage) { + if (messages.length === 0) { + throw new Error(`Found zero log ${description}s. And so, expected "${description} ${expectedMessage}" was not logged.`); + } + + let isFound = false; + messages.forEach(function(message, index) { + if (!isFound && message.includes(expectedMessage)) { + isFound = true; + // remove from the array now that it is found + messages.splice(index, 1); + } + }); + + if (!isFound) { + throw new Error(`Did not find any log ${description}s matching ${expectedMessage}. Found: ${messages.join('\n')}`); + } +} + +module.exports = { + assertWarning, + assertDeprecation +};