diff --git a/packages/migrate/src/index.ts b/packages/migrate/src/index.ts index ceaae74b1ac..dcc03269b32 100644 --- a/packages/migrate/src/index.ts +++ b/packages/migrate/src/index.ts @@ -133,15 +133,11 @@ function runMigration(currentConfigPath: string, outputConfigPath: string): Prom return; } - runPrettier(outputConfigPath, result, (err: object): void => { - if (err) { - throw err; - } - }); + runPrettier(outputConfigPath, result); if (answer.confirmValidation) { - const outputPath = await import(outputConfigPath); - const webpackOptionsValidationErrors: string[] = validate(outputPath); + const outputConfig = (await import(outputConfigPath)).default; + const webpackOptionsValidationErrors: string[] = validate(outputConfig); if (webpackOptionsValidationErrors.length) { console.error(chalk.red("\n✖ Your configuration validation wasn't successful \n")); diff --git a/packages/utils/src/run-prettier.ts b/packages/utils/src/run-prettier.ts index 99b12858428..41eb61dcc6b 100644 --- a/packages/utils/src/run-prettier.ts +++ b/packages/utils/src/run-prettier.ts @@ -8,35 +8,27 @@ import prettier from 'prettier'; * * @param {String} outputPath - Path to write the config to * @param {Node} source - AST to write at the given path - * @param {Function} cb - executes a callback after execution if supplied - * @returns {Void} Writes a file at given location and prints messages accordingly + * @returns {Void} Writes a file at given location */ -export function runPrettier(outputPath: string, source: string, cb?: Function): void { - function validateConfig(): void | Function { - let prettySource: string; - let error: object; - try { - prettySource = prettier.format(source, { - filepath: outputPath, - parser: 'babel', - singleQuote: true, - tabWidth: 1, - useTabs: true, - }); - } catch (err) { - process.stdout.write( - `\n${chalk.yellow( - `WARNING: Could not apply prettier to ${outputPath}` + ' due validation error, but the file has been created\n', - )}`, - ); - prettySource = source; - error = err; - } - if (cb) { - return cb(error); - } - return fs.writeFileSync(outputPath, prettySource, 'utf8'); +export function runPrettier(outputPath: string, source: string): void { + let prettySource: string = source; + try { + prettySource = prettier.format(source, { + filepath: outputPath, + parser: 'babel', + singleQuote: true, + tabWidth: 1, + useTabs: true, + }); + } catch (err) { + process.stdout.write( + `\n${chalk.yellow( + `WARNING: Could not apply prettier to ${outputPath}` + ' due validation error, but the file has been created\n', + )}`, + ); + prettySource = source; } - return fs.writeFile(outputPath, source, 'utf8', validateConfig); + + return fs.writeFileSync(outputPath, prettySource, 'utf8'); } diff --git a/test/init/generator/init-inquirer.test.js b/test/init/generator/init-inquirer.test.js index 0402c8edf72..2c97c9fd71b 100644 --- a/test/init/generator/init-inquirer.test.js +++ b/test/init/generator/init-inquirer.test.js @@ -22,7 +22,7 @@ describe('init', () => { }); it('should scaffold when given answers', async () => { - const stdout = await runPromptWithAnswers(genPath, ['init'], ['N', ENTER, ENTER, ENTER, ENTER, ENTER, ENTER, ENTER]); + const { stdout } = await runPromptWithAnswers(genPath, ['init'], ['N', ENTER, ENTER, ENTER, ENTER, ENTER, ENTER, ENTER]); expect(stdout).toBeTruthy(); expect(stdout).toContain(firstPrompt); diff --git a/test/loader/loader.test.js b/test/loader/loader.test.js index 0aec72825d8..49e625a9a5e 100644 --- a/test/loader/loader.test.js +++ b/test/loader/loader.test.js @@ -31,7 +31,7 @@ describe('loader command', () => { }); it('should scaffold loader template with a given name', async () => { - const stdout = await runPromptWithAnswers(__dirname, ['loader'], [loaderName, ENTER]); + const { stdout } = await runPromptWithAnswers(__dirname, ['loader'], [loaderName, ENTER]); expect(stdout).toContain(firstPrompt); diff --git a/test/migrate/config/bad-webpack.config.js b/test/migrate/config/bad-webpack.config.js new file mode 100644 index 00000000000..5bec4e124d8 --- /dev/null +++ b/test/migrate/config/bad-webpack.config.js @@ -0,0 +1,7 @@ +/* eslint-disable */ + +module.exports = { + output: { + badOption: true, + }, +}; diff --git a/test/migrate/config/migrate-config.test.js b/test/migrate/config/migrate-config.test.js index a9d75fbcc00..583cb05efaf 100644 --- a/test/migrate/config/migrate-config.test.js +++ b/test/migrate/config/migrate-config.test.js @@ -1,10 +1,26 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); +const rimraf = require('rimraf'); const { run, runAndGetWatchProc, runPromptWithAnswers } = require('../../utils/test-utils'); const ENTER = '\x0D'; +const outputDir = 'test-assets'; +const outputPath = path.join(__dirname, outputDir); +const outputFile = `${outputDir}/updated-webpack.config.js`; +const outputFilePath = path.join(__dirname, outputFile); describe('migrate command', () => { + beforeEach(() => { + rimraf.sync(outputPath); + fs.mkdirSync(outputPath); + }); + + afterAll(() => { + rimraf.sync(outputPath); + }); + it('should warn if the source config file is not specified', () => { const { stderr } = run(__dirname, ['migrate'], false); expect(stderr).toContain('Please specify a path to your webpack config'); @@ -21,12 +37,23 @@ describe('migrate command', () => { }); it('should prompt for config validation when an output path is provided', async () => { - const { stdout } = await runAndGetWatchProc(__dirname, ['migrate', 'webpack.config.js', 'updated-webpack.config.js'], false, 'y'); + const { stdout } = await runAndGetWatchProc(__dirname, ['migrate', 'webpack.config.js', outputFile], false, 'y'); expect(stdout).toContain('? Do you want to validate your configuration?'); }); it('should generate an updated config file when an output path is provided', async () => { - const stdout = await runPromptWithAnswers(__dirname, ['migrate', 'webpack.config.js', 'updated-webpack.config.js'], [ENTER, ENTER]); + const { stdout, stderr } = await runPromptWithAnswers(__dirname, ['migrate', 'webpack.config.js', outputFile], [ENTER, ENTER]); expect(stdout).toContain('? Do you want to validate your configuration?'); + expect(stderr).toBeFalsy(); + + expect(fs.existsSync(outputFilePath)).toBeTruthy(); + }); + + it('should generate an updated config file and warn of an invalid webpack config', async () => { + const { stdout, stderr } = await runPromptWithAnswers(__dirname, ['migrate', 'bad-webpack.config.js', outputFile], [ENTER, ENTER]); + expect(stdout).toContain('? Do you want to validate your configuration?'); + expect(stderr).toContain("configuration.output has an unknown property 'badOption'"); + + expect(fs.existsSync(outputFilePath)).toBeTruthy(); }); }); diff --git a/test/migrate/config/webpack.config.js b/test/migrate/config/webpack.config.js index f7f743e63a1..850b097b20c 100644 --- a/test/migrate/config/webpack.config.js +++ b/test/migrate/config/webpack.config.js @@ -1,8 +1,5 @@ /* eslint-disable */ -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); -const webpack = require('webpack'); module.exports = { entry: { @@ -16,47 +13,7 @@ module.exports = { path: path.resolve(__dirname, 'dist'), }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - - use: [{ - loader: 'babel-loader', - - options: { - presets: ['@babel/preset-env'], - } - }] - }, - { - test: /\.(scss|css)$/, - use: ExtractTextPlugin.extract({ - fallback: 'style', - use: 'css!sass' - }), - }, - ], - }, - - plugins: [new ExtractTextPlugin('styles-[contentHash].css'), new HtmlWebpackPlugin()], - optimization: { minimize: true }, - - optimizations: { - splitChunks: { - cacheGroups: { - common: { - name: 'common', - chunks: 'initial', - enforce: true, - minChunks: 2, - filename: 'common-[hash].min.js' - } - } - } - } -}; \ No newline at end of file +}; diff --git a/test/utils/test-utils.js b/test/utils/test-utils.js index d3676ff8c33..db969b8cef9 100644 --- a/test/utils/test-utils.js +++ b/test/utils/test-utils.js @@ -73,12 +73,18 @@ function runAndGetWatchProc(testCase, args = [], setOutput = true, input = '') { const outputPath = path.resolve(testCase, 'bin'); const argsWithOutput = setOutput ? args.concat('--output', outputPath) : args; - const webpackProc = execa(WEBPACK_PATH, argsWithOutput, { + const options = { cwd, - input, reject: false, stdio: ENABLE_LOG_COMPILATION ? 'inherit' : 'pipe', - }); + }; + + // some tests don't work if the input option is an empty string + if (input) { + options.input = input; + } + + const webpackProc = execa(WEBPACK_PATH, argsWithOutput, options); return webpackProc; } @@ -108,9 +114,26 @@ const runPromptWithAnswers = async (location, args, answers) => { }); return new Promise((resolve) => { + const obj = {}; + let stdoutDone = false; + let stderrDone = false; runner.stdout.pipe( concat((result) => { - resolve(result.toString()); + stdoutDone = true; + obj.stdout = result.toString(); + if (stderrDone) { + resolve(obj); + } + }), + ); + + runner.stderr.pipe( + concat((result) => { + stderrDone = true; + obj.stderr = result.toString(); + if (stdoutDone) { + resolve(obj); + } }), ); });