diff --git a/lib/cmd/compile/scripts.js b/lib/cmd/compile/scripts.js index c7ef760..8787bdd 100644 --- a/lib/cmd/compile/scripts.js +++ b/lib/cmd/compile/scripts.js @@ -53,7 +53,7 @@ const _ = require('lodash'), }, babelConfig = { // force babel to resolve the preset from claycli's node modules rather than the clay install's repo - presets: [[require('@babel/preset-env'), { targets: helpers.browserslist }]] + presets: [[require('@babel/preset-env'), { targets: helpers.getConfigFileOrBrowsersList('babelTargets') }]] }, temporaryIDs = {}; @@ -324,7 +324,7 @@ function buildScripts(entries, options = {}) { babel: babelConfig, postcss: [ cssImport(), - autoprefixer(helpers.browserslist), + autoprefixer(helpers.getConfigFileOrBrowsersList('autoprefixerOptions')), mixins(), nested(), simpleVars() diff --git a/lib/cmd/compile/styles.js b/lib/cmd/compile/styles.js index 02ff5c2..f853157 100644 --- a/lib/cmd/compile/styles.js +++ b/lib/cmd/compile/styles.js @@ -115,7 +115,7 @@ function compile(options = {}) { .pipe(rename(renameFile)) .pipe(postcss([ cssImport(), - autoprefixer(helpers.browserslist), + autoprefixer(helpers.getConfigFileOrBrowsersList('autoprefixerOptions')), mixins(), // Simple vars must come before `nested` so that string interpolation of variables occurs before // the nesting is parsed. This ensures being able to use variables in class names of nested selectors diff --git a/lib/compilation-helpers.js b/lib/compilation-helpers.js index abc2565..e6a2000 100644 --- a/lib/compilation-helpers.js +++ b/lib/compilation-helpers.js @@ -4,7 +4,8 @@ const format = require('date-fns/format'), chalk = require('chalk'), fs = require('fs-extra'), path = require('path'), - amphoraFs = require('amphora-fs'); + amphoraFs = require('amphora-fs'), + configFile = require('./config-file-helpers'); /** * determine how long a compilation task took @@ -131,15 +132,15 @@ function transformPath(prefix, destPath, shouldMinify) { * @returns {Array} */ function determinePostCSSPlugins(argv) { - const config = amphoraFs.tryRequire(`${process.cwd()}/claycli.config`); + const configPlugins = configFile.getConfigValue('plugins'); - if (config && config.plugins) { - if (!Array.isArray(config.plugins)) { + if (configPlugins) { + if (!Array.isArray(configPlugins)) { console.error(`${chalk.red('Error: Plugins supplied in config file is not an array')}`); } // Return the array of plugins defined in the config file - return config.plugins; + return configPlugins; } else { return _.map(argv.plugins, (pluginName) => { const plugin = amphoraFs.tryRequire(pluginName); @@ -156,6 +157,19 @@ function determinePostCSSPlugins(argv) { } } +/** + * Given an key, grab the value from the config file + * or pull from the browserlist that's supported + * + * @param {String} key + * @returns {Object|String} + */ +function getConfigFileOrBrowsersList(key) { + const configFileValue = configFile.getConfigValue(key); + + return configFileValue ? configFileValue : module.exports.browserslist; +} + module.exports.time = time; module.exports.debouncedWatcher = _.debounce(watcher, 200); module.exports.bucket = bucket; @@ -165,6 +179,7 @@ module.exports.hasChanged = hasChanged; module.exports.transformPath = transformPath; module.exports.browserslist = { browsers: ['> 3%', 'not and_uc > 0'] }; // used by styles, and vueify, and babel/preset-env module.exports.determinePostCSSPlugins = determinePostCSSPlugins; +module.exports.getConfigFileOrBrowsersList = getConfigFileOrBrowsersList; // for testing module.exports.watcher = watcher; diff --git a/lib/compilation-helpers.test.js b/lib/compilation-helpers.test.js index 285891e..403e61d 100644 --- a/lib/compilation-helpers.test.js +++ b/lib/compilation-helpers.test.js @@ -2,10 +2,12 @@ const lib = require('./compilation-helpers'), amphoraFs = require('amphora-fs'), + configFile = require('./config-file-helpers'), mockConsole = require('jest-mock-console').default; -// Mock tryRequire +// Mocks amphoraFs.tryRequire = jest.fn(); +configFile.getConfigValue = jest.fn(); describe('compilation helpers', () => { describe('time', () => { @@ -120,18 +122,14 @@ describe('compilation helpers', () => { }); it('uses the values passed in from the command if no config file is set', () => { - amphoraFs.tryRequire - .mockReturnValueOnce(undefined) - .mockReturnValueOnce(pluginMock); + amphoraFs.tryRequire.mockReturnValue(pluginMock); fn({ plugins: [ 'some-val' ]}); expect(pluginMock).toHaveBeenCalled(); }); it('throws an error if the plugin cannot be found when required', () => { - amphoraFs.tryRequire - .mockReturnValueOnce(undefined) - .mockReturnValueOnce(undefined); + amphoraFs.tryRequire.mockReturnValue(undefined); expect(() => fn({ plugins: [ 'some-val' ]})).toThrowError(); }); @@ -139,9 +137,7 @@ describe('compilation helpers', () => { it('logs if the required plugin\'s invocation fails', () => { const restoreConsole = mockConsole(); - amphoraFs.tryRequire - .mockReturnValueOnce(undefined) - .mockReturnValueOnce(() => { throw new Error('foo'); }); + amphoraFs.tryRequire.mockReturnValue(() => { throw new Error('foo'); }); fn({ plugins: [ 'some-val' ]}); expect(console.error).toHaveBeenCalled(); @@ -149,22 +145,38 @@ describe('compilation helpers', () => { }); it ('returns the plugin array from the config file if it exists', () => { - amphoraFs.tryRequire - .mockReturnValueOnce({ plugins: [] }) - .mockReturnValueOnce(pluginMock); + const restoreConsole = mockConsole(); + + configFile.getConfigValue.mockReturnValue([]); + amphoraFs.tryRequire.mockReturnValue(pluginMock); fn({ plugins: [ 'some-val' ]}); - expect(amphoraFs.tryRequire).toHaveBeenCalledTimes(1); + expect(amphoraFs.tryRequire).toHaveBeenCalledTimes(0); expect(pluginMock).not.toHaveBeenCalled(); + restoreConsole(); }); it ('throws an error if the config file plugins property is not an array', () => { const restoreConsole = mockConsole(); - amphoraFs.tryRequire.mockReturnValueOnce({ plugins: {} }); + configFile.getConfigValue.mockReturnValue({}); fn({ plugins: [ 'some-val' ]}); expect(console.error).toHaveBeenCalled(); restoreConsole(); }); }); + + describe('getConfigFileOrBrowsersList', () => { + const fn = lib.getConfigFileOrBrowsersList; + + it('returns a value from the config if one is found', () => { + configFile.getConfigValue.mockReturnValue({}); + expect(fn('foo')).toEqual({}); + }); + + it('returns a value defined in the file no config value is found', () => { + configFile.getConfigValue.mockReturnValue(undefined); + expect(fn('foo')).toEqual(lib.browserslist); + }); + }); }); diff --git a/lib/config-file-helpers.js b/lib/config-file-helpers.js new file mode 100644 index 0000000..c765015 --- /dev/null +++ b/lib/config-file-helpers.js @@ -0,0 +1,35 @@ +'use strict'; + +const amphoraFs = require('amphora-fs'), + CONFIG_FILENAME = 'claycli.config'; +var CONFIG_FILE = getConfigFile(); + +/** + * Grab the config file from the working directory + * or return undefined + * + * @returns {Object|Undefined} + */ +function getConfigFile() { + return amphoraFs.tryRequire(`${process.cwd()}/${CONFIG_FILENAME}`); +} + +/** + * Return a value from the config file + * + * @param {String} key + * @returns {Any} + */ +function getConfigValue(key) { + if (!CONFIG_FILE) { + return undefined; + } + + return CONFIG_FILE[key]; +} + +module.exports.getConfigValue = getConfigValue; + +// For testing +module.exports.getConfigFile = getConfigFile; +module.exports.setConfigFile = val => CONFIG_FILE = val; diff --git a/lib/config-file-helpers.test.js b/lib/config-file-helpers.test.js new file mode 100644 index 0000000..2628c86 --- /dev/null +++ b/lib/config-file-helpers.test.js @@ -0,0 +1,50 @@ +'use strict'; + +const lib = require('./config-file-helpers'), + amphoraFs = require('amphora-fs'); + +// Mock tryRequire +amphoraFs.tryRequire = jest.fn(); + +describe('project specific config file helpers', () => { + describe('getConfigFile', () => { + const fn = lib.getConfigFile; + + it('returns undefined if the file is not found', () => { + expect(fn()).toBe(undefined); + }); + + it('returns a file if one is found', () => { + amphoraFs.tryRequire.mockReturnValue({}); + + expect(fn()).toEqual({}); + }); + }); + + describe('getConfigValue', () => { + const fn = lib.getConfigValue, + SAMPLE_CONFIG = { + babelTargets: 'some value', + autoprefixerOptions: { foo: true, bar: false } + }; + + beforeEach(() => { + lib.setConfigFile(SAMPLE_CONFIG); + }); + + it('returns undefined if the config file is not present', () => { + lib.setConfigFile(undefined); + + expect(fn('babelTargets')).toBe(undefined); + }); + + it('returns a value from the config if it exists', () => { + expect(fn('babelTargets')).toBe('some value'); + expect(fn('autoprefixerOptions')).toEqual({ foo: true, bar: false}); + }); + + it('returns undefined if the value does not exist', () => { + expect(fn('fakeVal')).toBe(undefined); + }); + }); +});