From bd6cc170183a74018303867c84ae127ba40ee45c Mon Sep 17 00:00:00 2001 From: Bradley Maier Date: Fri, 24 Apr 2020 19:50:49 -0700 Subject: [PATCH] Dont show stats for bootstrap/runtime bundles. Update tests --- src/logger.ts | 45 ++++--- tests/unit/logger.ts | 304 +++++++++++++++++++++++++++++++------------ tests/unit/main.ts | 21 +-- 3 files changed, 247 insertions(+), 123 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 04228e1e..0c4b7fa0 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -19,6 +19,7 @@ export default function logger(stats: any, config: any, runningMessage: string = let chunks: undefined | string[]; let chunkMap: { [chunk: string]: any }; + const excludeChunks = /(^bootstrap$)|(^runtime\/)/; if (args.mode === 'dist') { chunkMap = analyzeBundles(stats, config, { analyzerMode: 'static', @@ -26,36 +27,38 @@ export default function logger(stats: any, config: any, runningMessage: string = generateStatsFile: true, reportFilename: '../info/report.html', statsFilename: '../info/stats.json', - excludeBundles: '(^bootstrap.)|(^runtime/)' + excludeBundles: '(^bootstrap\\.)|(^runtime/)' }); } chunks = (Array.isArray(config) ? loggerStats.children.reduce((chunks: any[], current: any) => [...chunks, ...current.chunks], []) : loggerStats.chunks - ).map((chunk: any) => { - const chunkName: string = chunk.names[0]; - if (!chunkMap) { - return chunkName; - } else { - const chunkStats = chunkMap[chunkName]; - const size = ((chunkStats && (chunkStats.parsedSize || chunkStats.statSize)) || 0) / 1000; - const gzipSize = ((chunkStats && chunkStats.gzipSize) || 0) / 1000; + ) + .filter((chunk: any) => !excludeChunks.test(chunk.names[0] || '')) + .map((chunk: any) => { + const chunkName: string = chunk.names[0]; + if (!chunkMap) { + return chunkName; + } else { + const chunkStats = chunkMap[chunkName]; + const size = ((chunkStats && (chunkStats.parsedSize || chunkStats.statSize)) || 0) / 1000; + const gzipSize = ((chunkStats && chunkStats.gzipSize) || 0) / 1000; - const chunkInfo = `${chunkName} ${chalk.yellow(`(${size}kB)`)}${ - gzipSize ? `/ ${chalk.blue(`(${gzipSize}kB gz)`)}` : '' - }`; + const chunkInfo = `${chunkName} ${chalk.yellow(`(${size}kB)`)}${ + gzipSize ? ` / ${chalk.blue(`(${gzipSize}kB gz)`)}` : '' + }`; - if (size > 250) { - const largestPackage = findLargestPackage(chunkStats); - if (largestPackage) { - return `${chunkInfo}\nLargest dependency is ${largestPackage.name} (${chalk.yellow( - `${largestPackage.size / 1000}kB` - )})`; + if (size > 250) { + const largestPackage = findLargestPackage(chunkStats); + if (largestPackage) { + return `${chunkInfo}\nLargest dependency is ${largestPackage.name} ${chalk.yellow( + `(${largestPackage.size / 1000}kB)` + )}`; + } } + return chunkInfo; } - return chunkInfo; - } - }); + }); let errors = ''; let warnings = ''; diff --git a/tests/unit/logger.ts b/tests/unit/logger.ts index 08ff136a..7795c410 100644 --- a/tests/unit/logger.ts +++ b/tests/unit/logger.ts @@ -14,27 +14,29 @@ let mockModule: MockModule; function assertOutput(isServing = false, hasManifest = true) { const logger = mockModule.getModuleUnderTest().default; const runningMessage = isServing ? 'running...' : undefined; + const toJson = sinon.stub().returns({ + hash: 'hash', + assets: [ + { + name: 'assetOne.js', + size: 1000 + }, + { + name: 'assetOne.js', + size: 1000 + } + ], + chunks: [ + { + names: ['chunkOne'] + } + ], + errors: [], + warnings: [] + }); + const hasErrors = logger( - { - hash: 'hash', - assets: [ - { - name: 'assetOne.js', - size: 1000 - }, - { - name: 'assetOne.js', - size: 1000 - } - ], - chunks: [ - { - names: ['chunkOne'] - } - ], - errors: [], - warnings: [] - }, + { toJson }, { output: { path: path.join(__dirname, '..', 'fixtures') @@ -43,18 +45,13 @@ function assertOutput(isServing = false, hasManifest = true) { runningMessage ); - let assetOne = `assetOne.js ${chalk.yellow('(0.03kb)')} / ${chalk.blue('(0.04kb gz)')}`; let signOff = chalk.green('The build completed successfully.'); if (runningMessage) { signOff += `\n\n${runningMessage}`; } - let chunksAndAssets = ''; - if (hasManifest) { - chunksAndAssets = `${chalk.yellow('chunks:')} -${columns(['chunkOne'])} -${chalk.yellow('assets:')} -${columns([assetOne, assetOne])}`; - } + let chunks = ''; + chunks = `${chalk.yellow('chunks:')} +${columns(['chunkOne'])}`; const expectedLog = ` ${logSymbols.info} cli-build-app: 9.9.9 @@ -63,7 +60,7 @@ ${logSymbols.success} hash: hash ${logSymbols.error} errors: 0 ${logSymbols.warning} warnings: 0 ${''}${''} -${chunksAndAssets} +${chunks} ${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures')}`))}`)} ${signOff} @@ -71,14 +68,40 @@ ${signOff} const mockedLogUpdate = mockModule.getMock('log-update').ctor; assert.isTrue(mockedLogUpdate.calledWith(expectedLog)); assert.isFalse(hasErrors); + + const [{ warningsFilter }] = toJson.firstCall.args; + assert.isTrue(warningsFilter('[mini-css-extract-plugin]\nConflicting order between')); + assert.isFalse(warningsFilter('[mini-css-extract-plugin]')); + assert.isFalse(warningsFilter('')); + assert.isFalse(warningsFilter('some other warning')); } describe('logger', () => { beforeEach(() => { mockModule = new MockModule('../../src/logger', require); - mockModule.dependencies(['typescript', 'jsonfile', 'log-update']); + mockModule.dependencies([ + 'typescript', + 'jsonfile', + 'log-update', + '@dojo/webpack-contrib/webpack-bundle-analyzer/AnalyzeBundles', + '@dojo/webpack-contrib/webpack-bundle-analyzer/parseUtils' + ]); mockModule.getMock('jsonfile').readFileSync = sinon.stub().returns({ version: '9.9.9' }); mockModule.getMock('typescript').version = '1.1.1'; + mockModule.getMock( + '@dojo/webpack-contrib/webpack-bundle-analyzer/AnalyzeBundles' + ).default = sinon.stub().returns({ + chunkOne: { + parsedSize: 251 * 1000, + gzipSize: 800 + } + }); + mockModule.getMock( + '@dojo/webpack-contrib/webpack-bundle-analyzer/parseUtils' + ).findLargestPackage = sinon.stub().returns({ + size: 1000, + name: 'foo' + }); }); afterEach(() => { @@ -98,35 +121,32 @@ describe('logger', () => { assertOutput(true); }); - it('logging output without manifest', () => { - sinon.stub(fs, 'existsSync').returns(false); - assertOutput(false, false); - }); - it('logging output with errors', () => { const errors: any = ['error', 'otherError']; const warnings: any = ['warning', 'otherWarning']; const logger = mockModule.getModuleUnderTest().default; const hasErrors = logger( { - hash: 'hash', - assets: [ - { - name: 'assetOne.js', - size: 1000 - }, - { - name: 'assetOne.js', - size: 1000 - } - ], - chunks: [ - { - names: ['chunkOne'] - } - ], - errors, - warnings + toJson: () => ({ + hash: 'hash', + assets: [ + { + name: 'assetOne.js', + size: 1000 + }, + { + name: 'assetOne.js', + size: 1000 + } + ], + chunks: [ + { + names: ['chunkOne'] + } + ], + errors, + warnings + }) }, { output: { @@ -152,11 +172,6 @@ ${logSymbols.warning} warnings: 2 ${expectedErrors}${expectedWarnings} ${chalk.yellow('chunks:')} ${columns(['chunkOne'])} -${chalk.yellow('assets:')} -${columns([ - `assetOne.js ${chalk.yellow('(0.03kb)')} / ${chalk.blue('(0.04kb gz)')}`, - `assetOne.js ${chalk.yellow('(0.03kb)')} / ${chalk.blue('(0.04kb gz)')}` - ])} ${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures')}`))}`)} ${chalk.red('The build completed with errors.')} @@ -167,34 +182,101 @@ ${chalk.red('The build completed with errors.')} assert.isTrue(hasErrors); }); - it('should skip assets that do not exist', () => { + it('logging output in dist mode with a large chunk', () => { const logger = mockModule.getModuleUnderTest().default; const hasErrors = logger( { - hash: 'hash', - assets: [ - { - name: 'assetOne.js', - size: 1000 - }, - { - name: 'assetTwo.js', - size: 1000 - } - ], - chunks: [ - { - names: ['chunkOne'] - } - ], - errors: [], - warnings: [] + toJson: () => ({ + hash: 'hash', + assets: [ + { + name: 'assetOne.js', + size: 1000 + }, + { + name: 'assetOne.js', + size: 1000 + } + ], + chunks: [ + { + names: ['chunkOne'] + } + ], + errors: [], + warnings: [] + }) }, { output: { - path: path.join(__dirname, '..', 'fixtures', 'missing-assets') + path: path.join(__dirname, '..', 'fixtures') } + }, + '', + { mode: 'dist' } + ); + + const expectedLog = ` +${logSymbols.info} cli-build-app: 9.9.9 +${logSymbols.info} typescript: 1.1.1 +${logSymbols.success} hash: hash +${logSymbols.error} errors: 0 +${logSymbols.warning} warnings: 0 +${''}${''} +${chalk.yellow('chunks:')} +${columns([ + `chunkOne ${chalk.yellow('(251kB)')} / ${chalk.blue( + '(0.8kB gz)' + )}\nLargest dependency is foo ${chalk.yellow('(1kB)')}` + ])} +${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures')}`))}`)} + +${chalk.green('The build completed successfully.')} + `; + const mockedLogUpdate = mockModule.getMock('log-update').ctor; + assert.strictEqual(mockedLogUpdate.firstCall.args[0], expectedLog); + assert.isTrue(mockedLogUpdate.calledWith(expectedLog)); + assert.isFalse(hasErrors); + }); + + it('logging output in dist mode with a small chunk', () => { + mockModule.getMock('@dojo/webpack-contrib/webpack-bundle-analyzer/AnalyzeBundles').default.returns({ + chunkOne: { + parsedSize: 249 * 1000, + gzipSize: 800 } + }); + const logger = mockModule.getModuleUnderTest().default; + const hasErrors = logger( + { + toJson: () => ({ + hash: 'hash', + assets: [ + { + name: 'assetOne.js', + size: 1000 + }, + { + name: 'assetOne.js', + size: 1000 + } + ], + chunks: [ + { + names: ['chunkOne'] + } + ], + errors: [], + warnings: [] + }) + }, + { + output: { + path: path.join(__dirname, '..', 'fixtures') + } + }, + '', + { mode: 'dist' } ); const expectedLog = ` @@ -205,14 +287,64 @@ ${logSymbols.error} errors: 0 ${logSymbols.warning} warnings: 0 ${''}${''} ${chalk.yellow('chunks:')} -${columns(['chunkOne'])} -${chalk.yellow('assets:')} -${columns([`assetOne.js ${chalk.yellow('(0.03kb)')} / ${chalk.blue('(0.04kb gz)')}`])} -${chalk.yellow( - `output at: ${chalk.cyan( - chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures', 'missing-assets')}`) - )}` - )} +${columns([`chunkOne ${chalk.yellow('(249kB)')} / ${chalk.blue('(0.8kB gz)')}`])} +${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures')}`))}`)} + +${chalk.green('The build completed successfully.')} + `; + const mockedLogUpdate = mockModule.getMock('log-update').ctor; + assert.strictEqual(mockedLogUpdate.firstCall.args[0], expectedLog); + assert.isTrue(mockedLogUpdate.calledWith(expectedLog)); + assert.isFalse(hasErrors); + }); + + it('logging output in dist mode when unable to find packages', () => { + mockModule + .getMock('@dojo/webpack-contrib/webpack-bundle-analyzer/parseUtils') + .findLargestPackage.returns(undefined); + const logger = mockModule.getModuleUnderTest().default; + const hasErrors = logger( + { + toJson: () => ({ + hash: 'hash', + assets: [ + { + name: 'assetOne.js', + size: 1000 + }, + { + name: 'assetOne.js', + size: 1000 + } + ], + chunks: [ + { + names: ['chunkOne'] + } + ], + errors: [], + warnings: [] + }) + }, + { + output: { + path: path.join(__dirname, '..', 'fixtures') + } + }, + '', + { mode: 'dist' } + ); + + const expectedLog = ` +${logSymbols.info} cli-build-app: 9.9.9 +${logSymbols.info} typescript: 1.1.1 +${logSymbols.success} hash: hash +${logSymbols.error} errors: 0 +${logSymbols.warning} warnings: 0 +${''}${''} +${chalk.yellow('chunks:')} +${columns([`chunkOne ${chalk.yellow('(251kB)')} / ${chalk.blue('(0.8kB gz)')}`])} +${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${path.join(__dirname, '..', 'fixtures')}`))}`)} ${chalk.green('The build completed successfully.')} `; diff --git a/tests/unit/main.ts b/tests/unit/main.ts index daa1612a..1951f68b 100644 --- a/tests/unit/main.ts +++ b/tests/unit/main.ts @@ -137,7 +137,7 @@ describe('command', () => { const main = mockModule.getModuleUnderTest().default; return main.run(getMockHelper(), { mode: 'dist' }).then(() => { assert.isTrue(mockDistConfig.called); - assert.isTrue(mockLogger.calledWith('stats', 'dist config')); + assert.isTrue(mockLogger.calledWith(stats, 'dist config')); }); }); @@ -145,7 +145,7 @@ describe('command', () => { const main = mockModule.getModuleUnderTest().default; return main.run(getMockHelper(), { mode: 'unit' }).then(() => { assert.isTrue(mockUnitTestConfig.called); - assert.isTrue(mockLogger.calledWith('stats', 'unit config')); + assert.isTrue(mockLogger.calledWith(stats, 'unit config')); }); }); @@ -153,7 +153,7 @@ describe('command', () => { const main = mockModule.getModuleUnderTest().default; return main.run(getMockHelper(), { mode: 'functional' }).then(() => { assert.isTrue(mockFunctionalTestConfig.called); - assert.isTrue(mockLogger.calledWith('stats', 'functional config')); + assert.isTrue(mockLogger.calledWith(stats, 'functional config')); }); }); @@ -161,7 +161,7 @@ describe('command', () => { const main = mockModule.getModuleUnderTest().default; return main.run(getMockHelper(), { mode: 'test' }).then(() => { assert.isTrue(mockUnitTestConfig.called); - assert.isTrue(mockLogger.calledWith('stats', 'unit config')); + assert.isTrue(mockLogger.calledWith(stats, 'unit config')); assert.isTrue(consoleWarnStub.calledOnce); assert.isTrue( consoleWarnStub.calledWith( @@ -180,17 +180,6 @@ describe('command', () => { }); }); - it('filters CSS module order warnings from the logger', () => { - const main = mockModule.getModuleUnderTest().default; - return main.run(getMockHelper(), { mode: 'unit' }).then(() => { - const [{ warningsFilter }] = stats.toJson.firstCall.args; - assert.isTrue(warningsFilter('[mini-css-extract-plugin]\nConflicting order between')); - assert.isFalse(warningsFilter('[mini-css-extract-plugin]')); - assert.isFalse(warningsFilter('')); - assert.isFalse(warningsFilter('some other warning')); - }); - }); - it('rejects if an error occurs', () => { isError = true; const main = mockModule.getModuleUnderTest().default; @@ -260,7 +249,7 @@ describe('command', () => { invalidHookStub.callsFake((name: string, callback: Function) => callback(filename)); return main.run(getMockHelper(), { watch: true }).then(() => { - assert.isTrue(mockLogger.calledWith('stats', 'dist config', 'watching...')); + assert.isTrue(mockLogger.calledWith(stats, 'dist config', 'watching...')); }); }); });