Skip to content

Commit

Permalink
Remove lodash (tailwindlabs#5390)
Browse files Browse the repository at this point in the history
* remove `lodash` usage

* implement custom cloneDeep to replace lodash's

* drop lodash in processPlugins

* add `toPath` utility

* add `tap` utility

* add `cloneDeep` utility

* drop lodash in evaluateTailwindFunctions

* add `defaults` utility

* drop lodash from `resolveConfig`

* remove `lodash` dependency
  • Loading branch information
RobinMalfait authored Sep 6, 2021
1 parent a9e160c commit a34bd62
Show file tree
Hide file tree
Showing 28 changed files with 218 additions and 208 deletions.
4 changes: 2 additions & 2 deletions defaultConfig.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const cloneDeep = require('lodash/cloneDeep')
const defaultConfig = require('./stubs/defaultConfig.stub.js')
let { cloneDeep } = require('./src/util/cloneDeep')
let defaultConfig = require('./stubs/defaultConfig.stub.js')

module.exports = cloneDeep(defaultConfig)
4 changes: 2 additions & 2 deletions defaultTheme.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const cloneDeep = require('lodash/cloneDeep')
const defaultConfig = require('./stubs/defaultConfig.stub.js')
let { cloneDeep } = require('./src/util/cloneDeep')
let defaultConfig = require('./stubs/defaultConfig.stub.js')

module.exports = cloneDeep(defaultConfig.theme)
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
"fast-glob": "^3.2.7",
"glob-parent": "^6.0.1",
"is-glob": "^4.0.1",
"lodash": "^4.17.21",
"normalize-path": "^3.0.0",
"object-hash": "^2.2.0",
"postcss-js": "^3.0.3",
Expand Down
7 changes: 3 additions & 4 deletions src/featureFlags.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'lodash'
import chalk from 'chalk'
import log from './util/log'

Expand All @@ -9,11 +8,11 @@ const featureFlags = {

export function flagEnabled(config, flag) {
if (featureFlags.future.includes(flag)) {
return config.future === 'all' || _.get(config, ['future', flag], false)
return config.future === 'all' || (config?.future?.[flag] ?? false)
}

if (featureFlags.experimental.includes(flag)) {
return config.experimental === 'all' || _.get(config, ['experimental', flag], false)
return config.experimental === 'all' || (config?.experimental?.[flag] ?? false)
}

return false
Expand All @@ -24,7 +23,7 @@ function experimentalFlagsEnabled(config) {
return featureFlags.experimental
}

return Object.keys(_.get(config, 'experimental', {})).filter(
return Object.keys(config?.experimental ?? {}).filter(
(flag) => featureFlags.experimental.includes(flag) && config.experimental[flag]
)
}
Expand Down
35 changes: 21 additions & 14 deletions src/lib/evaluateTailwindFunctions.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import _ from 'lodash'
import dlv from 'dlv'
import didYouMean from 'didyoumean'
import transformThemeValue from '../util/transformThemeValue'
import parseValue from 'postcss-value-parser'
import buildMediaQuery from '../util/buildMediaQuery'
import { toPath } from '../util/toPath'

function isObject(input) {
return typeof input === 'object' && input !== null
}

function findClosestExistingPath(theme, path) {
const parts = _.toPath(path)
let parts = toPath(path)
do {
parts.pop()

if (_.hasIn(theme, parts)) break
if (dlv(theme, parts) !== undefined) break
} while (parts.length)

return parts.length ? parts : undefined
Expand All @@ -32,20 +37,22 @@ function listKeys(obj) {
}

function validatePath(config, path, defaultValue) {
const pathString = Array.isArray(path) ? pathToString(path) : _.trim(path, `'"`)
const pathSegments = Array.isArray(path) ? path : _.toPath(pathString)
const value = _.get(config.theme, pathString, defaultValue)
const pathString = Array.isArray(path)
? pathToString(path)
: path.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
const pathSegments = Array.isArray(path) ? path : toPath(pathString)
const value = dlv(config.theme, pathString, defaultValue)

if (typeof value === 'undefined') {
if (value === undefined) {
let error = `'${pathString}' does not exist in your theme config.`
const parentSegments = pathSegments.slice(0, -1)
const parentValue = _.get(config.theme, parentSegments)
const parentValue = dlv(config.theme, parentSegments)

if (_.isObject(parentValue)) {
if (isObject(parentValue)) {
const validKeys = Object.keys(parentValue).filter(
(key) => validatePath(config, [...parentSegments, key]).isValid
)
const suggestion = didYouMean(_.last(pathSegments), validKeys)
const suggestion = didYouMean(pathSegments[pathSegments.length - 1], validKeys)
if (suggestion) {
error += ` Did you mean '${pathToString([...parentSegments, suggestion])}'?`
} else if (validKeys.length > 0) {
Expand All @@ -56,8 +63,8 @@ function validatePath(config, path, defaultValue) {
} else {
const closestPath = findClosestExistingPath(config.theme, pathString)
if (closestPath) {
const closestValue = _.get(config.theme, closestPath)
if (_.isObject(closestValue)) {
const closestValue = dlv(config.theme, closestPath)
if (isObject(closestValue)) {
error += ` '${pathToString(closestPath)}' has the following keys: ${listKeys(
closestValue
)}`
Expand Down Expand Up @@ -87,7 +94,7 @@ function validatePath(config, path, defaultValue) {
) {
let error = `'${pathString}' was found but does not resolve to a string.`

if (_.isObject(value)) {
if (isObject(value)) {
let validKeys = Object.keys(value).filter(
(key) => validatePath(config, [...pathSegments, key]).isValid
)
Expand Down Expand Up @@ -165,7 +172,7 @@ export default function ({ tailwindConfig: config }) {
return value
},
screen: (node, screen) => {
screen = _.trim(screen, `'"`)
screen = screen.replace(/^['"]+/g, '').replace(/['"]+$/g, '')

if (config.theme.screens[screen] === undefined) {
throw node.error(`The '${screen}' screen does not exist in your theme.`)
Expand Down
39 changes: 1 addition & 38 deletions src/lib/setupContextUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,7 @@ import bigSign from '../util/bigSign'
import corePlugins from '../corePlugins'
import * as sharedState from './sharedState'
import { env } from './sharedState'

function toPath(value) {
if (Array.isArray(value)) {
return value
}

let inBrackets = false
let parts = []
let chunk = ''

for (let i = 0; i < value.length; i++) {
let char = value[i]
if (char === '[') {
inBrackets = true
parts.push(chunk)
chunk = ''
continue
}
if (char === ']' && inBrackets) {
inBrackets = false
parts.push(chunk)
chunk = ''
continue
}
if (char === '.' && !inBrackets && chunk.length > 0) {
parts.push(chunk)
chunk = ''
continue
}
chunk = chunk + char
}

if (chunk.length > 0) {
parts.push(chunk)
}

return parts
}
import { toPath } from '../util/toPath'

function insertInto(list, value, { before = [] } = {}) {
before = [].concat(before)
Expand Down
28 changes: 14 additions & 14 deletions src/lib/substituteResponsiveAtRules.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import _ from 'lodash'
import postcss from 'postcss'
import cloneNodes from '../util/cloneNodes'
import buildMediaQuery from '../util/buildMediaQuery'
import buildSelectorVariant from '../util/buildSelectorVariant'
import { tap } from '../util/tap'

function isLayer(node) {
if (Array.isArray(node)) {
Expand All @@ -20,9 +20,9 @@ export default function (config) {
// Wrap any `responsive` rules with a copy of their parent `layer` to
// ensure the layer isn't lost when copying to the `screens` location.
css.walkAtRules('layer', (layerAtRule) => {
const layer = layerAtRule.params
let layer = layerAtRule.params
layerAtRule.walkAtRules('responsive', (responsiveAtRule) => {
const nestedlayerAtRule = postcss.atRule({
let nestedlayerAtRule = postcss.atRule({
name: 'layer',
params: layer,
})
Expand All @@ -32,15 +32,15 @@ export default function (config) {
})
})

const {
let {
theme: { screens },
separator,
} = config
const responsiveRules = postcss.root()
const finalRules = []
let responsiveRules = postcss.root()
let finalRules = []

css.walkAtRules('responsive', (atRule) => {
const nodes = atRule.nodes
let nodes = atRule.nodes
responsiveRules.append(...cloneNodes(nodes))

// If the parent is already a `layer` (this is true for anything coming from
Expand All @@ -55,16 +55,16 @@ export default function (config) {
atRule.remove()
})

_.keys(screens).forEach((screen) => {
const mediaQuery = postcss.atRule({
for (let [screen, value] of Object.entries(screens ?? {})) {
let mediaQuery = postcss.atRule({
name: 'media',
params: buildMediaQuery(screens[screen]),
params: buildMediaQuery(value),
})

mediaQuery.append(
_.tap(responsiveRules.clone(), (clonedRoot) => {
tap(responsiveRules.clone(), (clonedRoot) => {
clonedRoot.walkRules((rule) => {
rule.selectors = _.map(rule.selectors, (selector) =>
rule.selectors = rule.selectors.map((selector) =>
buildSelectorVariant(selector, screen, separator, (message) => {
throw rule.error(message)
})
Expand All @@ -74,9 +74,9 @@ export default function (config) {
)

finalRules.push(mediaQuery)
})
}

const hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)
let hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)

css.walkAtRules('tailwind', (atRule) => {
if (atRule.params !== 'screens') {
Expand Down
3 changes: 1 addition & 2 deletions src/lib/substituteScreenAtRules.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import _ from 'lodash'
import buildMediaQuery from '../util/buildMediaQuery'

export default function ({ tailwindConfig: { theme } }) {
return function (css) {
css.walkAtRules('screen', (atRule) => {
const screen = atRule.params

if (!_.has(theme.screens, screen)) {
if (!theme.screens?.hasOwnProperty?.(screen)) {
throw atRule.error(`No \`${screen}\` screen found.`)
}

Expand Down
Loading

0 comments on commit a34bd62

Please sign in to comment.