Remove lodash dependencies (#402)

* Remove lodash dependencies

Replace lodash.merge, lodash.castarray, and lodash.isplainobject with inline implementations to reduce bundle size and eliminate external dependencies. The merge function maintains full compatibility with lodash behavior, including recursive merging of nested objects within arrays.

- Remove lodash.merge, lodash.castarray, lodash.isplainobject from dependencies
- Add inline implementations in src/utils.js with identical behavior
- Update src/index.js to import utilities from utils module
- Maintain backward compatibility and all existing functionality

Fixes bundle size concerns raised in community discussions about lodash dependencies.

* Add changelog
This commit is contained in:
Philipp Spiess
2025-09-17 16:02:42 +02:00
committed by GitHub
parent d3c0dbd5df
commit f4651252ad
5 changed files with 69 additions and 48 deletions
+4
View File
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Include unit in `hr` border-width value ([#379](https://github.com/tailwindlabs/tailwindcss-typography/pull/379)
### Changed
- Remove lodash dependencies ([#402](https://github.com/tailwindlabs/tailwindcss-typography/pull/402))
## [0.5.16] - 2025-01-07
### Fixed
+3 -36
View File
@@ -1,17 +1,14 @@
{
"name": "@tailwindcss/typography",
"version": "0.5.14",
"version": "0.5.16",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@tailwindcss/typography",
"version": "0.5.14",
"version": "0.5.16",
"license": "MIT",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"devDependencies": {
@@ -30,7 +27,7 @@
"tailwindcss": "^3.2.2"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders"
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@ampproject/remapping": {
@@ -5293,21 +5290,6 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -11025,21 +11007,6 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU="
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-3
View File
@@ -48,9 +48,6 @@
"tailwindcss": "^3.2.2"
},
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"jest": {
+2 -7
View File
@@ -1,8 +1,6 @@
const plugin = require('tailwindcss/plugin')
const merge = require('lodash.merge')
const castArray = require('lodash.castarray')
const styles = require('./styles')
const { commonTrailingPseudos } = require('./utils')
const { commonTrailingPseudos, isObject, isPlainObject, merge, castArray } = require('./utils')
const computed = {
// Reserved for future "magic properties", for example:
@@ -25,10 +23,6 @@ function inWhere(selector, { className, modifier, prefix }) {
return `:where(${selectorPrefix}${selector}):not(:where([class~="${prefixedNot}"],[class~="${prefixedNot}"] *))`
}
function isObject(value) {
return typeof value === 'object' && value !== null
}
function configToCss(config = {}, { target, className, modifier, prefix }) {
function updateSelector(k, v) {
if (target === 'legacy') {
@@ -138,3 +132,4 @@ module.exports = plugin.withOptions(
}
}
)
+60 -2
View File
@@ -1,9 +1,67 @@
const isPlainObject = require('lodash.isplainobject')
const parser = require('postcss-selector-parser')
const parseSelector = parser()
function isObject(value) {
return typeof value === 'object' && value !== null
}
function isPlainObject(value) {
if (typeof value !== 'object' || value === null) {
return false
}
if (Object.prototype.toString.call(value) !== '[object Object]') {
return false
}
if (Object.getPrototypeOf(value) === null) {
return true
}
let proto = value
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(value) === proto
}
function merge(target, ...sources) {
if (!sources.length) return target
const source = sources.shift()
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (Array.isArray(source[key])) {
if (!target[key]) target[key] = []
source[key].forEach((item, index) => {
if (isPlainObject(item) && isPlainObject(target[key][index])) {
target[key][index] = merge(target[key][index], item)
} else {
target[key][index] = item
}
})
} else if (isPlainObject(source[key])) {
if (!target[key]) target[key] = {}
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
return merge(target, ...sources)
}
function castArray(value) {
return Array.isArray(value) ? value : [value]
}
module.exports = {
isObject,
isPlainObject,
merge,
castArray,
isUsableColor(color, values) {
return isPlainObject(values) && color !== 'gray' && values[600]
},