Skip to content

Commit a34bd62

Browse files
authored
Remove lodash (#5390)
* 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
1 parent a9e160c commit a34bd62

28 files changed

+218
-208
lines changed

defaultConfig.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const cloneDeep = require('lodash/cloneDeep')
2-
const defaultConfig = require('./stubs/defaultConfig.stub.js')
1+
let { cloneDeep } = require('./src/util/cloneDeep')
2+
let defaultConfig = require('./stubs/defaultConfig.stub.js')
33

44
module.exports = cloneDeep(defaultConfig)

defaultTheme.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const cloneDeep = require('lodash/cloneDeep')
2-
const defaultConfig = require('./stubs/defaultConfig.stub.js')
1+
let { cloneDeep } = require('./src/util/cloneDeep')
2+
let defaultConfig = require('./stubs/defaultConfig.stub.js')
33

44
module.exports = cloneDeep(defaultConfig.theme)

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"fast-glob": "^3.2.7",
7979
"glob-parent": "^6.0.1",
8080
"is-glob": "^4.0.1",
81-
"lodash": "^4.17.21",
8281
"normalize-path": "^3.0.0",
8382
"object-hash": "^2.2.0",
8483
"postcss-js": "^3.0.3",

src/featureFlags.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import _ from 'lodash'
21
import chalk from 'chalk'
32
import log from './util/log'
43

@@ -9,11 +8,11 @@ const featureFlags = {
98

109
export function flagEnabled(config, flag) {
1110
if (featureFlags.future.includes(flag)) {
12-
return config.future === 'all' || _.get(config, ['future', flag], false)
11+
return config.future === 'all' || (config?.future?.[flag] ?? false)
1312
}
1413

1514
if (featureFlags.experimental.includes(flag)) {
16-
return config.experimental === 'all' || _.get(config, ['experimental', flag], false)
15+
return config.experimental === 'all' || (config?.experimental?.[flag] ?? false)
1716
}
1817

1918
return false
@@ -24,7 +23,7 @@ function experimentalFlagsEnabled(config) {
2423
return featureFlags.experimental
2524
}
2625

27-
return Object.keys(_.get(config, 'experimental', {})).filter(
26+
return Object.keys(config?.experimental ?? {}).filter(
2827
(flag) => featureFlags.experimental.includes(flag) && config.experimental[flag]
2928
)
3029
}

src/lib/evaluateTailwindFunctions.js

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
import _ from 'lodash'
1+
import dlv from 'dlv'
22
import didYouMean from 'didyoumean'
33
import transformThemeValue from '../util/transformThemeValue'
44
import parseValue from 'postcss-value-parser'
55
import buildMediaQuery from '../util/buildMediaQuery'
6+
import { toPath } from '../util/toPath'
7+
8+
function isObject(input) {
9+
return typeof input === 'object' && input !== null
10+
}
611

712
function findClosestExistingPath(theme, path) {
8-
const parts = _.toPath(path)
13+
let parts = toPath(path)
914
do {
1015
parts.pop()
1116

12-
if (_.hasIn(theme, parts)) break
17+
if (dlv(theme, parts) !== undefined) break
1318
} while (parts.length)
1419

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

3439
function validatePath(config, path, defaultValue) {
35-
const pathString = Array.isArray(path) ? pathToString(path) : _.trim(path, `'"`)
36-
const pathSegments = Array.isArray(path) ? path : _.toPath(pathString)
37-
const value = _.get(config.theme, pathString, defaultValue)
40+
const pathString = Array.isArray(path)
41+
? pathToString(path)
42+
: path.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
43+
const pathSegments = Array.isArray(path) ? path : toPath(pathString)
44+
const value = dlv(config.theme, pathString, defaultValue)
3845

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

44-
if (_.isObject(parentValue)) {
51+
if (isObject(parentValue)) {
4552
const validKeys = Object.keys(parentValue).filter(
4653
(key) => validatePath(config, [...parentSegments, key]).isValid
4754
)
48-
const suggestion = didYouMean(_.last(pathSegments), validKeys)
55+
const suggestion = didYouMean(pathSegments[pathSegments.length - 1], validKeys)
4956
if (suggestion) {
5057
error += ` Did you mean '${pathToString([...parentSegments, suggestion])}'?`
5158
} else if (validKeys.length > 0) {
@@ -56,8 +63,8 @@ function validatePath(config, path, defaultValue) {
5663
} else {
5764
const closestPath = findClosestExistingPath(config.theme, pathString)
5865
if (closestPath) {
59-
const closestValue = _.get(config.theme, closestPath)
60-
if (_.isObject(closestValue)) {
66+
const closestValue = dlv(config.theme, closestPath)
67+
if (isObject(closestValue)) {
6168
error += ` '${pathToString(closestPath)}' has the following keys: ${listKeys(
6269
closestValue
6370
)}`
@@ -87,7 +94,7 @@ function validatePath(config, path, defaultValue) {
8794
) {
8895
let error = `'${pathString}' was found but does not resolve to a string.`
8996

90-
if (_.isObject(value)) {
97+
if (isObject(value)) {
9198
let validKeys = Object.keys(value).filter(
9299
(key) => validatePath(config, [...pathSegments, key]).isValid
93100
)
@@ -165,7 +172,7 @@ export default function ({ tailwindConfig: config }) {
165172
return value
166173
},
167174
screen: (node, screen) => {
168-
screen = _.trim(screen, `'"`)
175+
screen = screen.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
169176

170177
if (config.theme.screens[screen] === undefined) {
171178
throw node.error(`The '${screen}' screen does not exist in your theme.`)

src/lib/setupContextUtils.js

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,7 @@ import bigSign from '../util/bigSign'
1515
import corePlugins from '../corePlugins'
1616
import * as sharedState from './sharedState'
1717
import { env } from './sharedState'
18-
19-
function toPath(value) {
20-
if (Array.isArray(value)) {
21-
return value
22-
}
23-
24-
let inBrackets = false
25-
let parts = []
26-
let chunk = ''
27-
28-
for (let i = 0; i < value.length; i++) {
29-
let char = value[i]
30-
if (char === '[') {
31-
inBrackets = true
32-
parts.push(chunk)
33-
chunk = ''
34-
continue
35-
}
36-
if (char === ']' && inBrackets) {
37-
inBrackets = false
38-
parts.push(chunk)
39-
chunk = ''
40-
continue
41-
}
42-
if (char === '.' && !inBrackets && chunk.length > 0) {
43-
parts.push(chunk)
44-
chunk = ''
45-
continue
46-
}
47-
chunk = chunk + char
48-
}
49-
50-
if (chunk.length > 0) {
51-
parts.push(chunk)
52-
}
53-
54-
return parts
55-
}
18+
import { toPath } from '../util/toPath'
5619

5720
function insertInto(list, value, { before = [] } = {}) {
5821
before = [].concat(before)

src/lib/substituteResponsiveAtRules.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import _ from 'lodash'
21
import postcss from 'postcss'
32
import cloneNodes from '../util/cloneNodes'
43
import buildMediaQuery from '../util/buildMediaQuery'
54
import buildSelectorVariant from '../util/buildSelectorVariant'
5+
import { tap } from '../util/tap'
66

77
function isLayer(node) {
88
if (Array.isArray(node)) {
@@ -20,9 +20,9 @@ export default function (config) {
2020
// Wrap any `responsive` rules with a copy of their parent `layer` to
2121
// ensure the layer isn't lost when copying to the `screens` location.
2222
css.walkAtRules('layer', (layerAtRule) => {
23-
const layer = layerAtRule.params
23+
let layer = layerAtRule.params
2424
layerAtRule.walkAtRules('responsive', (responsiveAtRule) => {
25-
const nestedlayerAtRule = postcss.atRule({
25+
let nestedlayerAtRule = postcss.atRule({
2626
name: 'layer',
2727
params: layer,
2828
})
@@ -32,15 +32,15 @@ export default function (config) {
3232
})
3333
})
3434

35-
const {
35+
let {
3636
theme: { screens },
3737
separator,
3838
} = config
39-
const responsiveRules = postcss.root()
40-
const finalRules = []
39+
let responsiveRules = postcss.root()
40+
let finalRules = []
4141

4242
css.walkAtRules('responsive', (atRule) => {
43-
const nodes = atRule.nodes
43+
let nodes = atRule.nodes
4444
responsiveRules.append(...cloneNodes(nodes))
4545

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

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

6464
mediaQuery.append(
65-
_.tap(responsiveRules.clone(), (clonedRoot) => {
65+
tap(responsiveRules.clone(), (clonedRoot) => {
6666
clonedRoot.walkRules((rule) => {
67-
rule.selectors = _.map(rule.selectors, (selector) =>
67+
rule.selectors = rule.selectors.map((selector) =>
6868
buildSelectorVariant(selector, screen, separator, (message) => {
6969
throw rule.error(message)
7070
})
@@ -74,9 +74,9 @@ export default function (config) {
7474
)
7575

7676
finalRules.push(mediaQuery)
77-
})
77+
}
7878

79-
const hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)
79+
let hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)
8080

8181
css.walkAtRules('tailwind', (atRule) => {
8282
if (atRule.params !== 'screens') {

src/lib/substituteScreenAtRules.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import _ from 'lodash'
21
import buildMediaQuery from '../util/buildMediaQuery'
32

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

9-
if (!_.has(theme.screens, screen)) {
8+
if (!theme.screens?.hasOwnProperty?.(screen)) {
109
throw atRule.error(`No \`${screen}\` screen found.`)
1110
}
1211

0 commit comments

Comments
 (0)