Repository: maizzle/framework
Pull request #352 (2020-11-06 18:06:29)

refactor(core): use lodash
Switch to using `lodash` everywhere:

- remove `getPropValue`, `isObject`, `isEmptyObject` helpers
- remove `deepmerge` dependency


Patch:
diff --git a/package-lock.json b/package-lock.json
index 21f768c..564f1bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2839,11 +2839,6 @@
         "core-assert": "^0.2.0"
       }
     },
-    "deepmerge": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
-      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
-    },
     "default-require-extensions": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz",
diff --git a/package.json b/package.json
index d3d5405..7006d1d 100644
--- a/package.json
+++ b/package.json
@@ -36,13 +36,13 @@
     "browser-sync": "^2.26.12",
     "bs-html-injector": "^3.0.3",
     "color-shorthand-hex-to-six-digit": "^2.10.65",
-    "deepmerge": "^4.2.2",
     "email-comb": "^3.9.22",
     "front-matter": "^4.0.0",
     "fs-extra": "^9.0.0",
     "glob-promise": "^3.4.0",
     "html-crush": "^2.0.3",
     "juice": "^7.0.0",
+    "lodash": "^4.17.20",
     "ora": "^5.0.0",
     "postcss": "^8.1.2",
     "postcss-import": "^13.0.0",
diff --git a/src/generators/config.js b/src/generators/config.js
index 49d5d65..351f8d6 100644
--- a/src/generators/config.js
+++ b/src/generators/config.js
@@ -1,5 +1,5 @@
 const path = require('path')
-const deepmerge = require('deepmerge')
+const {merge} = require('lodash')
 
 module.exports = {
   getMerged: async (env = 'local') => {
@@ -24,6 +24,6 @@ module.exports = {
       }
     }
 
-    return deepmerge(baseConfig, envConfig)
+    return merge(baseConfig, envConfig)
   }
 }
diff --git a/src/generators/output/to-disk.js b/src/generators/output/to-disk.js
index 4800d33..4a934e9 100644
--- a/src/generators/output/to-disk.js
+++ b/src/generators/output/to-disk.js
@@ -1,8 +1,9 @@
 const path = require('path')
 const fs = require('fs-extra')
 const glob = require('glob-promise')
+const {get, isEmpty} = require('lodash')
+const {asyncForEach} = require('../../utils/helpers')
 const removePlaintextTags = require('../../transformers/plaintext')
-const {asyncForEach, getPropValue, isEmptyObject} = require('../../utils/helpers')
 
 const Config = require('../config')
 const Tailwind = require('../tailwind')
@@ -13,14 +14,14 @@ const render = require('./to-string')
 module.exports = async (env, spinner, config) => {
   process.env.NODE_ENV = env === 'local' ? 'local' : 'production'
 
-  if (isEmptyObject(config)) {
+  if (isEmpty(config)) {
     config = await Config.getMerged(env).catch(error => {
       spinner.fail('Build failed')
       throw error
     })
   }
 
-  const buildTemplates = getPropValue(config, 'build.templates')
+  const buildTemplates = get(config, 'build.templates')
   const templatesConfig = Array.isArray(buildTemplates) ? buildTemplates : [buildTemplates]
 
   const parsed = []
@@ -28,7 +29,7 @@ module.exports = async (env, spinner, config) => {
   const css = await Tailwind.compile('', '', {}, config)
 
   await asyncForEach(templatesConfig, async templateConfig => {
-    const outputDir = getPropValue(templateConfig, 'destination.path') || `build_${env}`
+    const outputDir = get(templateConfig, 'destination.path', `build_${env}`)
 
     await fs.remove(outputDir)
 
@@ -62,7 +63,7 @@ module.exports = async (env, spinner, config) => {
           })
             .then(async ({html, config}) => {
               const destination = config.permalink || file
-              const plaintextConfig = getPropValue(config, 'plaintext.enabled') || config.plaintext
+              const plaintextConfig = get(config, 'plaintext.enabled', config.plaintext)
               const isPlaintextEnabled = typeof plaintextConfig === 'boolean' && plaintextConfig
 
               if (isPlaintextEnabled) {
@@ -74,7 +75,7 @@ module.exports = async (env, spinner, config) => {
 
               await fs.outputFile(destination, html)
                 .then(async () => {
-                  const extension = getPropValue(templateConfig, 'destination.extension') || 'html'
+                  const extension = get(templateConfig, 'destination.extension', 'html')
 
                   if (extension !== 'html') {
                     const parts = path.parse(destination)
@@ -101,7 +102,7 @@ module.exports = async (env, spinner, config) => {
             })
         })
 
-        const assets = {source: '', destination: 'assets', ...getPropValue(templateConfig, 'assets')}
+        const assets = {source: '', destination: 'assets', ...get(templateConfig, 'assets')}
 
         if (Array.isArray(assets.source)) {
           await asyncForEach(assets.source, async source => {
diff --git a/src/generators/output/to-string.js b/src/generators/output/to-string.js
index 523a9a1..ad0668f 100644
--- a/src/generators/output/to-string.js
+++ b/src/generators/output/to-string.js
@@ -1,13 +1,12 @@
 const fm = require('front-matter')
-const deepmerge = require('deepmerge')
+const {get, merge} = require('lodash')
 const Tailwind = require('../tailwind')
 const posthtml = require('../posthtml')
 const Transformers = require('../../transformers')
-const {getPropValue} = require('../../utils/helpers')
 const posthtmlMso = require('../../transformers/posthtml-mso')
 
 module.exports = async (html, options) => {
-  process.env.NODE_ENV = getPropValue(options, 'maizzle.env') || 'local'
+  process.env.NODE_ENV = get(options, 'maizzle.env', 'local')
 
   if (typeof html !== 'string') {
     throw new TypeError(`first argument must be an HTML string, received ${html}`)
@@ -17,15 +16,15 @@ module.exports = async (html, options) => {
     throw new RangeError('received empty string')
   }
 
-  let config = getPropValue(options, 'maizzle') || {}
-  const tailwindConfig = getPropValue(options, 'tailwind.config') || {}
-  const cssString = getPropValue(options, 'tailwind.css') || '@tailwind components; @tailwind utilities;'
+  let config = get(options, 'maizzle', {})
+  const tailwindConfig = get(options, 'tailwind.config', {})
+  const cssString = get(options, 'tailwind.css', '@tailwind components; @tailwind utilities;')
 
   const frontMatter = fm(html)
   html = frontMatter.body
-  config = deepmerge(config, frontMatter.attributes)
+  config = merge(config, frontMatter.attributes)
 
-  if (typeof getPropValue(options, 'tailwind.compiled') === 'string') {
+  if (typeof get(options, 'tailwind.compiled') === 'string') {
     config.css = options.tailwind.compiled
   } else {
     config.css = await Tailwind.compile(cssString, html, tailwindConfig, config)
diff --git a/src/generators/plaintext.js b/src/generators/plaintext.js
index c3cdd4d..76c8956 100644
--- a/src/generators/plaintext.js
+++ b/src/generators/plaintext.js
@@ -1,11 +1,11 @@
 const path = require('path')
+const {get} = require('lodash')
 const stripHTML = require('string-strip-html')
-const {getPropValue} = require('../utils/helpers')
 
 module.exports.generate = async (html, destination, config) => {
-  destination = getPropValue(config, 'permalink') || destination
+  destination = get(config, 'permalink', destination)
 
-  const options = getPropValue(config, 'plaintext') || {}
+  const options = get(config, 'plaintext', {})
 
   const plaintext = stripHTML(html, {
     dumpLinkHrefsNearby: {
diff --git a/src/generators/posthtml.js b/src/generators/posthtml.js
index fb81c10..3e97172 100644
--- a/src/generators/posthtml.js
+++ b/src/generators/posthtml.js
@@ -1,29 +1,29 @@
+const {get} = require('lodash')
 const fm = require('front-matter')
 const posthtml = require('posthtml')
 const fetch = require('posthtml-fetch')
 const layouts = require('posthtml-extend')
 const modules = require('posthtml-modules')
-const {getPropValue} = require('../utils/helpers')
 const expressions = require('posthtml-expressions')
 
 module.exports = async (html, config) => {
-  const layoutsOptions = getPropValue(config, 'build.layouts') || {}
+  const layoutsOptions = get(config, 'build.layouts', {})
 
-  const fetchOptions = getPropValue(config, 'build.posthtml.fetch') || {}
+  const fetchOptions = get(config, 'build.posthtml.fetch', {})
   const fetchPlugin = fetch({...fetchOptions})
 
-  const modulesOptions = getPropValue(config, 'build.components') || {}
+  const modulesOptions = get(config, 'build.components', {})
   // Fake `from` option so we can reference modules relatively
   const modulesRoot = modulesOptions.root || './'
   const modulesFrom = modulesOptions.from || `${modulesRoot}/fake`
 
-  const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
-  const posthtmlPlugins = getPropValue(config, 'build.posthtml.plugins') || []
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
+  const posthtmlPlugins = get(config, 'build.posthtml.plugins', [])
 
-  const expressionsOptions = getPropValue(config, 'build.posthtml.expressions') || {}
+  const expressionsOptions = get(config, 'build.posthtml.expressions', {})
   const locals = {
-    ...getPropValue(expressionsOptions, 'locals') || {},
-    ...getPropValue(config, 'locals') || {},
+    ...get(expressionsOptions, 'locals', {}),
+    ...get(config, 'locals', {}),
     page: config
   }
   const expressionsPlugin = expressions({...expressionsOptions, locals})
diff --git a/src/generators/tailwind.js b/src/generators/tailwind.js
index 95b202b..8fbbf47 100644
--- a/src/generators/tailwind.js
+++ b/src/generators/tailwind.js
@@ -4,20 +4,21 @@ const postcss = require('postcss')
 const tailwind = require('tailwindcss')
 const atImport = require('postcss-import')
 const postcssNested = require('postcss-nested')
+const {get, isObject, isEmpty} = require('lodash')
+const {requireUncached} = require('../utils/helpers')
 const mergeLonghand = require('postcss-merge-longhand')
 const purgecss = require('@fullhuman/postcss-purgecss')
-const {getPropValue, isObject, isEmptyObject, requireUncached} = require('../utils/helpers')
 
 const defaultPurgeCSSExtractor = /[\w-/:%.]+(?<!:)/g
 
 module.exports = {
   compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}) => {
-    tailwindConfig = (isObject(tailwindConfig) && !isEmptyObject(tailwindConfig)) ? tailwindConfig : getPropValue(maizzleConfig, 'build.tailwind.config') || 'tailwind.config.js'
-    const tailwindConfigObject = (isObject(tailwindConfig) && !isEmptyObject(tailwindConfig)) ? tailwindConfig : requireUncached(path.resolve(process.cwd(), tailwindConfig))
+    tailwindConfig = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : get(maizzleConfig, 'build.tailwind.config', 'tailwind.config.js')
+    const tailwindConfigObject = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : requireUncached(path.resolve(process.cwd(), tailwindConfig))
 
-    const purgeCSSOptions = getPropValue(maizzleConfig, 'purgeCSS') || {}
+    const purgeCSSOptions = get(maizzleConfig, 'purgeCSS', {})
 
-    const buildTemplates = getPropValue(maizzleConfig, 'build.templates') || []
+    const buildTemplates = get(maizzleConfig, 'build.templates', [])
     const templateSources = Array.isArray(buildTemplates) ? buildTemplates.map(({source}) => `${source}/**/*.*`) : [buildTemplates].map(({source}) => `${source}/**/*.*`)
     const tailwindSources = Array.isArray(tailwindConfigObject.purge) ? tailwindConfigObject.purge : (isObject(tailwindConfigObject.purge) ? tailwindConfigObject.purge.content || [] : [])
 
@@ -33,9 +34,9 @@ module.exports = {
       {raw: html}
     ]
 
-    const extractor = getPropValue(tailwindConfigObject, 'purge.options.extractor') || purgeCSSOptions.extractor || defaultPurgeCSSExtractor
-    const purgeSafeList = getPropValue(tailwindConfigObject, 'purge.options.safelist') || purgeCSSOptions.safelist || {}
-    const purgeBlockList = getPropValue(tailwindConfigObject, 'purge.options.blocklist') || purgeCSSOptions.blocklist || []
+    const extractor = get(tailwindConfigObject, 'purge.options.extractor') || purgeCSSOptions.extractor || defaultPurgeCSSExtractor
+    const purgeSafeList = get(tailwindConfigObject, 'purge.options.safelist') || purgeCSSOptions.safelist || {}
+    const purgeBlockList = get(tailwindConfigObject, 'purge.options.blocklist') || purgeCSSOptions.blocklist || []
 
     const purgeCssPlugin = maizzleConfig.env === 'local' ? () => {} : purgecss({
       content: purgeSources,
@@ -46,15 +47,15 @@ module.exports = {
 
     const mergeLonghandPlugin = maizzleConfig.env === 'local' ? () => {} : mergeLonghand()
 
-    const tailwindPlugin = isEmptyObject(tailwindConfigObject) ? tailwind() : tailwind({
+    const tailwindPlugin = isEmpty(tailwindConfigObject) ? tailwind() : tailwind({
       important: true,
       purge: false,
       ...tailwindConfigObject
     })
 
-    const postcssUserPlugins = getPropValue(maizzleConfig, 'build.postcss.plugins') || []
+    const postcssUserPlugins = get(maizzleConfig, 'build.postcss.plugins', [])
 
-    const userFilePath = getPropValue(maizzleConfig, 'build.tailwind.css')
+    const userFilePath = get(maizzleConfig, 'build.tailwind.css')
 
     css = await fs.pathExists(userFilePath).then(async exists => {
       if (exists) {
diff --git a/src/index.js b/src/index.js
index 43f88d3..94d5bb9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,11 @@
 const ora = require('ora')
 const path = require('path')
 const fs = require('fs-extra')
-const deepmerge = require('deepmerge')
 const bs = require('browser-sync')
+const {get, merge} = require('lodash')
 const injector = require('bs-html-injector')
 const Output = require('./generators/output')
 const Plaintext = require('./generators/plaintext')
-const {getPropValue} = require('./utils/helpers')
 
 const self = module.exports = { // eslint-disable-line
   build: async (env = 'local', config = {}) => {
@@ -31,18 +30,18 @@ const self = module.exports = { // eslint-disable-line
         require('./generators/config')
           .getMerged('local')
           .then(localConfig => {
-            config = deepmerge(config, localConfig)
+            config = merge(config, localConfig)
 
-            let templates = getPropValue(config, 'build.templates')
+            let templates = get(config, 'build.templates')
             templates = Array.isArray(templates) ? templates : [templates]
 
-            const baseDir = getPropValue(templates[0], 'destination.path') || 'build_local'
+            const baseDir = get(templates[0], 'destination.path', 'build_local')
 
             const watchPaths = [
               'src/**/*.*',
-              getPropValue(config, 'build.tailwind.config') || 'tailwind.config.js',
-              ...new Set(templates.map(config => `${getPropValue(config, 'source') || 'src'}/**/*.*`)),
-              ...new Set(getPropValue(config, 'build.browsersync.watch'))
+              get(config, 'build.tailwind.config', 'tailwind.config.js'),
+              ...new Set(templates.map(config => `${get(config, 'source', 'src')}/**/*.*`)),
+              ...new Set(get(config, 'build.browsersync.watch', []))
             ]
 
             const bsOptions = {
@@ -55,7 +54,7 @@ const self = module.exports = { // eslint-disable-line
               },
               tunnel: false,
               ui: {port: 3001},
-              ...getPropValue(config, 'build.browsersync')
+              ...get(config, 'build.browsersync', {})
             }
 
             bs.create()
@@ -67,17 +66,15 @@ const self = module.exports = { // eslint-disable-line
             bs.watch(watchPaths)
               .on('change', async file => {
                 file = file.replace(/\\/g, '/')
-                const fileSource = getPropValue(templates.filter(v => path.dirname(file).replace(/\\/g, '/').includes(v.source))[0], 'source')
+                const fileSource = get(templates.filter(v => path.dirname(file).replace(/\\/g, '/').includes(v.source))[0], 'source')
 
                 // Only if this file is in one of the build.templates.source paths
                 if (templates.map(o => o.source).includes(fileSource)) {
                   const start = new Date()
                   const spinner = ora('Building email...').start()
 
-                  const destination = getPropValue(templates.filter(v => path.dirname(file).replace(/\\/g, '/').includes(v.source))[0], 'destination.path')
-                  const cssString = fs.existsSync(getPropValue(config, 'build.tailwind.css')) ?
-                    fs.readFileSync(getPropValue(config, 'build.tailwind.css')) :
-                    '@tailwind components; @tailwind utilities;'
+                  const destination = get(templates.filter(v => path.dirname(file).replace(/\\/g, '/').includes(v.source))[0], 'destination.path')
+                  const cssString = fs.existsSync(get(config, 'build.tailwind.css')) ? fs.readFileSync(get(config, 'build.tailwind.css')) : '@tailwind components; @tailwind utilities;'
 
                   if (config.events && typeof config.events.beforeCreate === 'function') {
                     await config.events.beforeCreate(config)
diff --git a/src/transformers/extra-attributes.js b/src/transformers/extra-attributes.js
index 708394f..b3d651e 100644
--- a/src/transformers/extra-attributes.js
+++ b/src/transformers/extra-attributes.js
@@ -1,5 +1,5 @@
 const posthtml = require('posthtml')
-const {isObject, getPropValue} = require('../utils/helpers')
+const {get, isObject} = require('lodash')
 const addAttributes = require('posthtml-extra-attributes')
 
 module.exports = async (html, config) => {
@@ -18,9 +18,9 @@ module.exports = async (html, config) => {
     attributes = {...attributes, ...config.extraAttributes}
   }
 
-  const options = getPropValue(config, 'build.posthtml.options') || {}
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
-  html = posthtml([addAttributes({attributes})]).process(html, options).then(result => result.html)
+  html = posthtml([addAttributes({attributes})]).process(html, posthtmlOptions).then(result => result.html)
 
   return html
 }
diff --git a/src/transformers/inline.js b/src/transformers/inline.js
index 500b958..fe35fe8 100644
--- a/src/transformers/inline.js
+++ b/src/transformers/inline.js
@@ -1,28 +1,25 @@
 const juice = require('juice')
 const posthtml = require('posthtml')
+const {get, isObject, isEmpty} = require('lodash')
 const mergeLonghand = require('posthtml-postcss-merge-longhand')
-const {isObject, isEmptyObject, getPropValue} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const options = getPropValue(config, 'inlineCSS') || {}
-  const removeStyleTags = typeof options.removeStyleTags === 'undefined' ? true : options.removeStyleTags
+  const options = get(config, 'inlineCSS', {})
+  const removeStyleTags = get(options, 'removeStyleTags', true)
 
-  if (!isEmptyObject(options) && options.enabled) {
-    juice.styleToAttribute = isObject(options.styleToAttribute) ? options.styleToAttribute : juice.styleToAttribute
+  if (get(options, 'enabled', false)) {
+    juice.styleToAttribute = get(options, 'styleToAttribute', juice.styleToAttribute)
 
     if (isObject(options.applySizeAttribute)) {
-      const widthElements = getPropValue(options, 'applySizeAttribute.width')
-      const heightElements = getPropValue(options, 'applySizeAttribute.height')
-
-      juice.widthElements = isObject(widthElements) ? Object.values(widthElements) : juice.widthElements
-      juice.heightElements = isObject(heightElements) ? Object.values(heightElements) : juice.heightElements
+      juice.widthElements = get(options, 'applySizeAttribute.width', juice.widthElements)
+      juice.heightElements = get(options, 'applySizeAttribute.height', juice.heightElements)
     }
 
-    if (isObject(options.excludedProperties)) {
-      juice.excludedProperties = isEmptyObject(options.excludedProperties) ? juice.excludedProperties : Object.values(options.excludedProperties)
+    if (!isEmpty(options.excludedProperties)) {
+      juice.excludedProperties = get(options, 'excludedProperties', Object.values(juice.excludedProperties))
     }
 
-    if (isObject(options.codeBlocks) && !isEmptyObject(options.codeBlocks)) {
+    if (isObject(options.codeBlocks) && !isEmpty(options.codeBlocks)) {
       Object.entries(options.codeBlocks).forEach(([k, v]) => {
         juice.codeBlocks[k] = v
       })
@@ -30,9 +27,9 @@ module.exports = async (html, config) => {
 
     html = juice(html, {removeStyleTags})
 
-    const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
-    const mergeLonghandConfig = getPropValue(options, 'mergeLonghand') || {enabled: false}
-    const tags = getPropValue(mergeLonghandConfig, 'tags') || []
+    const posthtmlOptions = get(config, 'build.posthtml.options', {})
+    const mergeLonghandConfig = get(options, 'mergeLonghand', {enabled: false})
+    const tags = get(mergeLonghandConfig, 'tags', [])
 
     if (mergeLonghandConfig.enabled || (typeof mergeLonghandConfig === 'boolean' && mergeLonghandConfig)) {
       html = await posthtml([mergeLonghand({tags})]).process(html, posthtmlOptions).then(result => result.html)
diff --git a/src/transformers/markdown.js b/src/transformers/markdown.js
index b15b793..0e68c1a 100644
--- a/src/transformers/markdown.js
+++ b/src/transformers/markdown.js
@@ -1,12 +1,11 @@
 const posthtml = require('posthtml')
+const {get, merge} = require('lodash')
 const markdown = require('posthtml-markdownit')
-const {getPropValue} = require('../utils/helpers')
-const deepmerge = require('deepmerge')
 
 module.exports = async (html, config) => {
-  const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
-  const userMarkdownOptions = getPropValue(config, 'markdown') || {}
-  const markdownOptions = deepmerge({markdownit: {html: true}}, userMarkdownOptions)
+  const userMarkdownOptions = get(config, 'markdown', {})
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
+  const markdownOptions = merge({markdownit: {html: true}}, userMarkdownOptions)
 
   return posthtml([
     markdown({...markdownOptions})
diff --git a/src/transformers/minify.js b/src/transformers/minify.js
index 828fccb..0cd4122 100644
--- a/src/transformers/minify.js
+++ b/src/transformers/minify.js
@@ -1,8 +1,8 @@
+const {get} = require('lodash')
 const {crush} = require('html-crush')
-const {isObject} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  if (isObject(config.minify) && config.minify.enabled) {
+  if (get(config, 'minify.enabled', false)) {
     html = crush(html, {removeLineBreaks: true, ...config.minify}).result
   }
 
diff --git a/src/transformers/plaintext.js b/src/transformers/plaintext.js
index 9239f61..6a5f729 100644
--- a/src/transformers/plaintext.js
+++ b/src/transformers/plaintext.js
@@ -1,8 +1,8 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
-const {getPropValue} = require('../utils/helpers')
 
 module.exports = (html, config) => {
-  const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
   return posthtml([plaintext()]).process(html, {...posthtmlOptions, sync: true}).html
 }
diff --git a/src/transformers/posthtml-mso.js b/src/transformers/posthtml-mso.js
index 61dc934..5e5f6af 100644
--- a/src/transformers/posthtml-mso.js
+++ b/src/transformers/posthtml-mso.js
@@ -1,10 +1,10 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
 const outlook = require('posthtml-mso')
-const {getPropValue} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const outlookOptions = getPropValue(config, 'build.posthtml.outlook') || {}
-  const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
+  const outlookOptions = get(config, 'build.posthtml.outlook', {})
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
   return posthtml([outlook({...outlookOptions})]).process(html, {...posthtmlOptions}).then(result => result.html)
 }
diff --git a/src/transformers/prevent-widows.js b/src/transformers/prevent-widows.js
index 47e80c2..dd9ff90 100644
--- a/src/transformers/prevent-widows.js
+++ b/src/transformers/prevent-widows.js
@@ -1,9 +1,9 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
 const preventWidows = require('prevent-widows')
-const {getPropValue} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const options = getPropValue(config, 'build.posthtml.options') || {}
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
-  return posthtml([preventWidows.posthtml()]).process(html, options).then(result => result.html)
+  return posthtml([preventWidows.posthtml()]).process(html, posthtmlOptions).then(result => result.html)
 }
diff --git a/src/transformers/remove-attributes.js b/src/transformers/remove-attributes.js
index e36247d..b9e17b4 100644
--- a/src/transformers/remove-attributes.js
+++ b/src/transformers/remove-attributes.js
@@ -1,10 +1,10 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
 const removeAttributes = require('posthtml-remove-attributes')
-const {getPropValue, isObject} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const options = getPropValue(config, 'build.posthtml.options') || {}
-  const attributes = isObject(config.removeAttributes) ? config.removeAttributes : []
+  const attributes = get(config, 'removeAttributes', [])
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
   attributes.push({name: 'style'})
 
@@ -13,7 +13,7 @@ module.exports = async (html, config) => {
     attr.value = attr.value || ''
   })
 
-  html = await posthtml([removeAttributes(attributes)]).process(html, options).then(result => result.html)
+  html = await posthtml([removeAttributes(attributes)]).process(html, posthtmlOptions).then(result => result.html)
 
   return html
 }
diff --git a/src/transformers/remove-inline-bgcolor.js b/src/transformers/remove-inline-bgcolor.js
index 12b08ad..6108812 100644
--- a/src/transformers/remove-inline-bgcolor.js
+++ b/src/transformers/remove-inline-bgcolor.js
@@ -1,18 +1,18 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
 const parseAttrs = require('posthtml-attrs-parser')
-const {getPropValue, isObject} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const prefers = getPropValue(config, 'inlineCSS.preferBgColorAttribute')
-  const options = getPropValue(config, 'build.posthtml.options') || {}
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
+  const prefers = get(config, 'inlineCSS.preferBgColorAttribute', false)
 
   if ((typeof prefers === 'boolean' && prefers)) {
-    return posthtml([removeInlineBGColor()]).process(html, options).then(result => result.html)
+    return posthtml([removeInlineBGColor()]).process(html, posthtmlOptions).then(result => result.html)
   }
 
-  if (isObject(prefers) && prefers.enabled) {
-    const tags = getPropValue(prefers, 'tags')
-    return posthtml([removeInlineBGColor({tags})]).process(html, options).then(result => result.html)
+  if (get(prefers, 'enabled')) {
+    const tags = get(prefers, 'tags')
+    return posthtml([removeInlineBGColor({tags})]).process(html, posthtmlOptions).then(result => result.html)
   }
 
   return html
diff --git a/src/transformers/remove-inline-sizes.js b/src/transformers/remove-inline-sizes.js
index 5ff437f..df58a72 100644
--- a/src/transformers/remove-inline-sizes.js
+++ b/src/transformers/remove-inline-sizes.js
@@ -1,13 +1,13 @@
 const posthtml = require('posthtml')
+const {get, isEmpty} = require('lodash')
 const parseAttrs = require('posthtml-attrs-parser')
-const {getPropValue, isEmptyObject} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  const settings = getPropValue(config, 'inlineCSS.keepOnlyAttributeSizes') || {}
+  const settings = get(config, 'inlineCSS.keepOnlyAttributeSizes', {})
 
-  if (!isEmptyObject(settings)) {
-    const options = getPropValue(config, 'build.posthtml.options') || {}
-    html = await posthtml([removeInlineSizes(settings)]).process(html, options).then(result => result.html)
+  if (!isEmpty(settings)) {
+    const posthtmlOptions = get(config, 'build.posthtml.options', {})
+    html = await posthtml([removeInlineSizes(settings)]).process(html, posthtmlOptions).then(result => result.html)
   }
 
   return html
diff --git a/src/transformers/replace-strings.js b/src/transformers/replace-strings.js
index 63f1289..000269b 100644
--- a/src/transformers/replace-strings.js
+++ b/src/transformers/replace-strings.js
@@ -1,7 +1,7 @@
-const {isObject, isEmptyObject} = require('../utils/helpers')
+const {isObject, isEmpty} = require('lodash')
 
 module.exports = async (html, config) => {
-  if (isObject(config.replaceStrings) && !isEmptyObject(config.replaceStrings)) {
+  if (isObject(config.replaceStrings) && !isEmpty(config.replaceStrings)) {
     Object.entries(config.replaceStrings).forEach(([k, v]) => {
       const regex = new RegExp(k, 'gi')
       html = html.replace(regex, v)
diff --git a/src/transformers/safe-class-names.js b/src/transformers/safe-class-names.js
index 03cb9f5..b9ada12 100644
--- a/src/transformers/safe-class-names.js
+++ b/src/transformers/safe-class-names.js
@@ -1,5 +1,5 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
-const {getPropValue} = require('../utils/helpers')
 const safeClassNames = require('posthtml-safe-class-names')
 
 module.exports = async (html, config) => {
@@ -8,10 +8,10 @@ module.exports = async (html, config) => {
   }
 
   if (typeof config.env === 'string' && config.env !== 'local') {
-    const replacements = config.safeClassNames || {}
-    const options = getPropValue(config, 'build.posthtml.options') || {}
+    const replacements = get(config, 'safeClassNames', {})
+    const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
-    html = posthtml([safeClassNames({replacements})]).process(html, options).then(result => result.html)
+    html = posthtml([safeClassNames({replacements})]).process(html, posthtmlOptions).then(result => result.html)
   }
 
   return html
diff --git a/src/transformers/transform.js b/src/transformers/transform.js
index 264920b..74309f3 100644
--- a/src/transformers/transform.js
+++ b/src/transformers/transform.js
@@ -1,13 +1,13 @@
+const {get} = require('lodash')
 const posthtml = require('posthtml')
 const Tailwind = require('../generators/tailwind')
 const posthtmlContent = require('posthtml-content')
-const {getPropValue} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
   const replacements = config.transform || {}
-  const options = getPropValue(config, 'build.posthtml.options') || {}
+  const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
   replacements.postcss = css => Tailwind.compile(css, html, {}, config)
 
-  return posthtml([posthtmlContent(replacements)]).process(html, options).then(result => result.html)
+  return posthtml([posthtmlContent(replacements)]).process(html, posthtmlOptions).then(result => result.html)
 }
diff --git a/src/transformers/url-params.js b/src/transformers/url-params.js
index 980d35f..3742716 100644
--- a/src/transformers/url-params.js
+++ b/src/transformers/url-params.js
@@ -1,12 +1,14 @@
 const posthtml = require('posthtml')
+const {get, isObject, isEmpty} = require('lodash')
 const urlParams = require('posthtml-url-parameters')
-const {getPropValue, isObject, isEmptyObject} = require('../utils/helpers')
 
 module.exports = async (html, config) => {
-  if (isObject(config.urlParameters) && !isEmptyObject(config.urlParameters)) {
-    const {_options, ...parameters} = config.urlParameters
+  const urlParameters = get(config, 'urlParameters', {})
+
+  if (isObject(urlParameters) && !isEmpty(urlParameters)) {
+    const {_options, ...parameters} = urlParameters
     const {tags, qs} = _options || {tags: ['a'], qs: {encode: false}}
-    const posthtmlOptions = getPropValue(config, 'build.posthtml.options') || {}
+    const posthtmlOptions = get(config, 'build.posthtml.options', {})
 
     return posthtml([urlParams({parameters, tags, qs})]).process(html, posthtmlOptions).then(result => result.html)
   }
diff --git a/src/utils/helpers.js b/src/utils/helpers.js
index 2f27333..9477e1c 100644
--- a/src/utils/helpers.js
+++ b/src/utils/helpers.js
@@ -4,9 +4,6 @@ module.exports = {
       await callback(array[index], index, array) // eslint-disable-line
     }
   },
-  isObject: object => typeof object === 'object' && object !== null,
-  isEmptyObject: object => Object.entries(object).length === 0 && object.constructor === Object,
-  getPropValue: (object, path = '') => path.split('.').reduce((o, x) => o === undefined ? o : o[x], object),
   requireUncached: module => {
     try {
       delete require.cache[require.resolve(module)]


Test patch:
diff --git a/test/test-misc.js b/test/test-misc.js
index 6ce013a..149cd49 100644
--- a/test/test-misc.js
+++ b/test/test-misc.js
@@ -1,22 +1,8 @@
 const test = require('ava')
 const path = require('path')
-const Utils = require('../src/utils/helpers')
+const {requireUncached} = require('../src/utils/helpers')
 
 test('requires an uncached module', t => {
-  const helpers = Utils.requireUncached(path.resolve(process.cwd(), 'src/utils/helpers'))
+  const helpers = requireUncached(path.resolve(process.cwd(), 'src/utils/helpers'))
   t.is(typeof helpers.requireUncached, 'function')
 })
-
-test('gets nested object value using dot notation', t => {
-  const foo = {
-    bar: {
-      baz: 'qux'
-    }
-  }
-
-  const baz = Utils.getPropValue(foo, 'bar.baz')
-  const bax = Utils.getPropValue({}, 'bar.baz')
-
-  t.is(baz, 'qux')
-  t.is(typeof bax, 'undefined')
-})