diff --git a/packages/slate-config/README.md b/packages/slate-config/README.md index 0458304cf..598d4a67f 100644 --- a/packages/slate-config/README.md +++ b/packages/slate-config/README.md @@ -17,7 +17,7 @@ module.exports = { ...commonPaths // Configuration values can be set directly - 'babel.enable': true, + 'webpack.babel.enable': true, // Or computed when the schema file is require()'d by another file 'paths.theme': process.cwd(), diff --git a/packages/slate-tools/package.json b/packages/slate-tools/package.json index f2d018948..8870cb9ef 100755 --- a/packages/slate-tools/package.json +++ b/packages/slate-tools/package.json @@ -80,5 +80,8 @@ "write-file-webpack-plugin": "4.2.0", "yamljs": "0.2.10", "yargs": "^11.0.0" + }, + "devDependencies": { + "mock-fs": "^4.6.0" } } diff --git a/packages/slate-tools/slate-tools.schema.js b/packages/slate-tools/slate-tools.schema.js index 3ee2dec1c..8c6137a7c 100644 --- a/packages/slate-tools/slate-tools.schema.js +++ b/packages/slate-tools/slate-tools.schema.js @@ -5,59 +5,22 @@ const commonPaths = require('@shopify/slate-config/common/paths.schema'); module.exports = { ...commonPaths, - // Paths to exclude for all webpack loaders - 'webpack.commonExcludes': ['node_modules', 'assets/static/'], - - // Enable/disable Babel for theme scripts - 'babel.enable': true, - - // A path to a valid Babel configuration - 'babel.configPath': (config) => - path.join(config.get('paths.theme'), '.babelrc'), - - // Optimization settings for the cssnano plugin - 'cssnano.settings': {zindex: false}, - // Enable/disable the prompt to skip uploading settings_data.json 'cli.promptSettings': true, - // Object which contains entrypoints used in webpack's config.entry key - 'webpack.entrypoints': { - static: path.resolve(__dirname, 'tools/webpack/static-files-glob.js'), - }, - - // Extends webpack development config using 'webpack-merge' - // https://www.npmjs.com/package/webpack-merge - 'webpack.config.extend.dev': {}, - - // Extends webpack production config using 'webpack-merge' - // https://www.npmjs.com/package/webpack-merge - 'webpack.config.extend.prod': {}, - - // Default port used for asset server. If it is not available, the next port - // that is available is used. - 'network.startPort': 3000, - // Path to Eslint bin executable 'eslint.bin': path.resolve(__dirname, 'node_modules/.bin/eslint'), - // Path to .eslintrc file - 'eslint.rc': (config) => path.resolve(config.get('paths.theme'), '.eslintrc'), - // Path to .eslintignore file 'eslint.ignore': (config) => path.resolve(config.get('paths.theme'), '.eslintignore'), - // Path to Eslint bin executable - 'stylelint.bin': path.resolve(__dirname, 'node_modules/.bin/stylelint'), - - // Path to .stylelintrc file - 'stylelint.rc': (config) => - path.resolve(config.get('paths.theme'), '.stylelintrc'), + // Path to .eslintrc file + 'eslint.rc': (config) => path.resolve(config.get('paths.theme'), '.eslintrc'), - // Path to .stylelintignore file - 'stylelint.ignore': (config) => - path.resolve(config.get('paths.theme'), '.stylelintignore'), + // Default port used for asset server. If it is not available, the next port + // that is available is used. + 'network.startPort': 3000, // Path to Prettier bin executable 'prettier.bin': path.resolve(__dirname, 'node_modules/.bin/prettier'), @@ -70,9 +33,6 @@ module.exports = { 'prettier.ignore': (config) => path.resolve(config.get('paths.theme'), '.prettierignore'), - // Path to Themelint bin executable - 'themelint.bin': path.resolve(__dirname, 'node_modules/.bin/theme-lint'), - // Path to self-signed SSL certificate which is used when developing // (browsersync, asset server) to avoid browsers rejecting requests based // on SSL @@ -82,4 +42,44 @@ module.exports = { // (browsersync, asset server) to avoid browsers rejecting requests based // on SSL 'ssl.key': path.resolve(os.homedir(), '.localhost_ssl/server.key'), + + // Path to Eslint bin executable + 'stylelint.bin': path.resolve(__dirname, 'node_modules/.bin/stylelint'), + + // Path to .stylelintrc file + 'stylelint.rc': (config) => + path.resolve(config.get('paths.theme'), '.stylelintrc'), + + // Path to .stylelintignore file + 'stylelint.ignore': (config) => + path.resolve(config.get('paths.theme'), '.stylelintignore'), + + // Path to Themelint bin executable + 'themelint.bin': path.resolve(__dirname, 'node_modules/.bin/theme-lint'), + + // Include babel-loader in the webpack core config + 'webpack.babel.enable': true, + + // A path to a valid Babel configuration. File must exist for Babel to be enabled. + 'webpack.babel.configPath': (config) => + path.join(config.get('paths.theme'), '.babelrc'), + + // Paths to exclude for all webpack loaders + 'webpack.commonExcludes': ['node_modules', 'assets/static/'], + + // Extends webpack development config using 'webpack-merge' + // https://www.npmjs.com/package/webpack-merge + 'webpack.config.extend.dev': {}, + + // Extends webpack production config using 'webpack-merge' + // https://www.npmjs.com/package/webpack-merge + 'webpack.config.extend.prod': {}, + + // Optimization settings for the cssnano plugin + 'webpack.cssnano.settings': {zindex: false}, + + // Object which contains entrypoints used in webpack's config.entry key + 'webpack.entrypoints': { + static: path.resolve(__dirname, 'tools/webpack/static-files-glob.js'), + }, }; diff --git a/packages/slate-tools/tools/webpack/config/core.js b/packages/slate-tools/tools/webpack/config/core.js index b20051289..3b13b0e2f 100755 --- a/packages/slate-tools/tools/webpack/config/core.js +++ b/packages/slate-tools/tools/webpack/config/core.js @@ -7,7 +7,6 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); const SlateConfig = require('@shopify/slate-config'); const commonExcludes = require('../common-excludes'); -const babelLoader = require('../loaders/babel-loader'); const {entrypointFiles} = require('../entrypoints'); const config = new SlateConfig(require('../../../slate-tools.schema')); @@ -88,8 +87,6 @@ module.exports = { module: { rules: [ - ...babelLoader(), - { test: /\.js$/, exclude: commonExcludes(), diff --git a/packages/slate-tools/tools/webpack/config/dev.js b/packages/slate-tools/tools/webpack/config/dev.js index ebc19c5a8..f968891e4 100755 --- a/packages/slate-tools/tools/webpack/config/dev.js +++ b/packages/slate-tools/tools/webpack/config/dev.js @@ -6,6 +6,7 @@ const autoprefixer = require('autoprefixer'); const SlateConfig = require('@shopify/slate-config'); const webpackCoreConfig = require('./core'); +const babel = require('./parts/babel'); const commonExcludes = require('../common-excludes'); const {templateFiles, layoutFiles} = require('../entrypoints'); const HtmlWebpackIncludeLiquidStylesPlugin = require('../html-webpack-include-chunks'); @@ -20,6 +21,7 @@ Object.keys(webpackCoreConfig.entry).forEach((name) => { module.exports = merge( webpackCoreConfig, + babel, { mode: 'development', diff --git a/packages/slate-tools/tools/webpack/config/parts/__tests__/babel.test.js b/packages/slate-tools/tools/webpack/config/parts/__tests__/babel.test.js new file mode 100644 index 000000000..7ba99bee5 --- /dev/null +++ b/packages/slate-tools/tools/webpack/config/parts/__tests__/babel.test.js @@ -0,0 +1,51 @@ +const mock = require('mock-fs'); + +beforeEach(() => { + global.slateUserConfig = {}; + jest.resetModules(); +}); + +afterEach(mock.restore); + +describe('returns an empty object', () => { + test(`if 'webpack.babel.enable' config is set to false`, () => { + global.slateUserConfig['webpack.babel.enable'] = false; + + const babelLoader = require('../babel'); + const results = babelLoader(); + + expect(typeof results).toBe('object'); + expect(Object.keys(results).length).toBe(0); + }); + test(`if file at 'webpack.babel.configPath' config does not exist`, () => { + const babelLoader = require('../babel'); + + mock(); + + const results = babelLoader(); + + expect(typeof results).toBe('object'); + expect(Object.keys(results).length).toBe(0); + }); +}); + +describe('returns a webpack config object containing babel-loader', () => { + test(`if 'webpack.babel.enable' config is true and file at 'webpack.babel.configPath' exists`, () => { + const babelConfigPath = 'some/path'; + + global.slateUserConfig['webpack.babel.configPath'] = babelConfigPath; + + const babelLoader = require('../babel'); + + mock({[babelConfigPath]: '{}'}); + + const results = babelLoader(); + + expect(typeof results).toBe('object'); + expect(results.module).toBeDefined(); + expect(results.module.rules).toBeDefined(); + expect(Array.isArray(results.module.rules)).toBeTruthy(); + expect(typeof results.module.rules[0]).toBe('object'); + expect(results.module.rules[0].options.extends).toBe(babelConfigPath); + }); +}); diff --git a/packages/slate-tools/tools/webpack/config/parts/babel.js b/packages/slate-tools/tools/webpack/config/parts/babel.js new file mode 100644 index 000000000..dd377e433 --- /dev/null +++ b/packages/slate-tools/tools/webpack/config/parts/babel.js @@ -0,0 +1,30 @@ +const fs = require('fs'); +const SlateConfig = require('@shopify/slate-config'); + +const commonExcludes = require('../../common-excludes'); +const config = new SlateConfig(require('../../../../slate-tools.schema')); + +module.exports = () => { + if ( + !fs.existsSync(config.get('webpack.babel.configPath')) || + !config.get('webpack.babel.enable') + ) { + return {}; + } + + return { + module: { + rules: [ + { + test: /\.js$/, + exclude: commonExcludes(), + loader: 'babel-loader', + options: { + babelrc: false, + extends: config.get('webpack.babel.configPath'), + }, + }, + ], + }, + }; +}; diff --git a/packages/slate-tools/tools/webpack/config/prod.js b/packages/slate-tools/tools/webpack/config/prod.js index f1383e39e..b029c7639 100755 --- a/packages/slate-tools/tools/webpack/config/prod.js +++ b/packages/slate-tools/tools/webpack/config/prod.js @@ -11,6 +11,7 @@ const SlateConfig = require('@shopify/slate-config'); const SlateLiquidAssetsPlugin = require('@shopify/html-webpack-liquid-asset-tags-plugin'); const SlateTagPlugin = require('@shopify/slate-tag-webpack-plugin'); +const babel = require('./parts/babel'); const commonExcludes = require('../common-excludes'); const webpackCoreConfig = require('./core'); const packageJson = require('../../../package.json'); @@ -26,6 +27,7 @@ const extractStyles = new ExtractTextPlugin({ module.exports = merge( webpackCoreConfig, + babel, { mode: 'production', devtool: 'hidden-source-map', @@ -61,7 +63,7 @@ module.exports = merge( sourceMap: true, plugins: [ autoprefixer, - cssnano(config.get('cssnano.settings')), + cssnano(config.get('webpack.cssnano.settings')), ], }, }, diff --git a/packages/slate-tools/tools/webpack/loaders/babel-loader.js b/packages/slate-tools/tools/webpack/loaders/babel-loader.js deleted file mode 100644 index 78eba16ce..000000000 --- a/packages/slate-tools/tools/webpack/loaders/babel-loader.js +++ /dev/null @@ -1,26 +0,0 @@ -const fs = require('fs'); -const SlateConfig = require('@shopify/slate-config'); - -const commonExcludes = require('../common-excludes'); -const config = new SlateConfig(require('../../../slate-tools.schema')); - -module.exports = () => { - if ( - !fs.existsSync(config.get('babel.configPath')) || - !config.get('babel.enable') - ) { - return []; - } - - return [ - { - test: /\.js$/, - exclude: commonExcludes(), - loader: 'babel-loader', - options: { - babelrc: false, - extends: config.get('babel.configPath'), - }, - }, - ]; -}; diff --git a/yarn.lock b/yarn.lock index 6aef0cb2f..344e7f5e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7486,6 +7486,10 @@ mock-fs@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.4.2.tgz#09dec5313f97095a450be6aa2ad8ab6738d63d6b" +mock-fs@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.6.0.tgz#d944ef4c3e03ceb4e8332b4b31b8ac254051c8ae" + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"