fix(shadcn): deps in cts projects (#8229)

* fix(shadcn): deps in cts projects

* fix: deps

* chore: add changelog
This commit is contained in:
shadcn
2025-09-16 17:54:44 +04:00
committed by GitHub
parent b9f3ce1988
commit 75dde2e646
4 changed files with 286 additions and 230 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
fix deps in cts projects

View File

@@ -68,8 +68,10 @@
"@babel/core": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/plugin-transform-typescript": "^7.28.0",
"@babel/preset-typescript": "^7.27.1",
"@dotenvx/dotenvx": "^1.48.4",
"@modelcontextprotocol/sdk": "^1.17.2",
"browserslist": "^4.26.2",
"commander": "^14.0.0",
"cosmiconfig": "^9.0.0",
"dedent": "^1.6.0",

View File

@@ -117,239 +117,228 @@ export async function resolveRegistryTree(
config: Config,
options: { useCache?: boolean } = {}
) {
try {
options = {
useCache: true,
...options,
options = {
useCache: true,
...options,
}
let payload: z.infer<typeof registryItemWithSourceSchema>[] = []
let allDependencyItems: z.infer<typeof registryItemWithSourceSchema>[] = []
let allDependencyRegistryNames: string[] = []
const uniqueNames = Array.from(new Set(names))
const results = await fetchRegistryItems(uniqueNames, config, options)
const resultMap = new Map<string, z.infer<typeof registryItemSchema>>()
for (let i = 0; i < results.length; i++) {
if (results[i]) {
resultMap.set(uniqueNames[i], results[i])
}
}
let payload: z.infer<typeof registryItemWithSourceSchema>[] = []
let allDependencyItems: z.infer<typeof registryItemWithSourceSchema>[] = []
let allDependencyRegistryNames: string[] = []
const uniqueNames = Array.from(new Set(names))
const results = await fetchRegistryItems(uniqueNames, config, options)
const resultMap = new Map<string, z.infer<typeof registryItemSchema>>()
for (let i = 0; i < results.length; i++) {
if (results[i]) {
resultMap.set(uniqueNames[i], results[i])
}
for (const [sourceName, item] of Array.from(resultMap.entries())) {
// Add source tracking
const itemWithSource: z.infer<typeof registryItemWithSourceSchema> = {
...item,
_source: sourceName,
}
payload.push(itemWithSource)
for (const [sourceName, item] of Array.from(resultMap.entries())) {
// Add source tracking
const itemWithSource: z.infer<typeof registryItemWithSourceSchema> = {
...item,
_source: sourceName,
}
payload.push(itemWithSource)
if (item.registryDependencies) {
// Resolve namespace syntax and set headers for dependencies
let resolvedDependencies = item.registryDependencies
if (item.registryDependencies) {
// Resolve namespace syntax and set headers for dependencies
let resolvedDependencies = item.registryDependencies
// Check for namespaced dependencies when no registries are configured
if (!config?.registries) {
const namespacedDeps = item.registryDependencies.filter(
(dep: string) => dep.startsWith("@")
)
if (namespacedDeps.length > 0) {
const { registry } = parseRegistryAndItemFromString(
namespacedDeps[0]
)
throw new RegistryNotConfiguredError(registry)
}
} else {
resolvedDependencies = resolveRegistryItemsFromRegistries(
item.registryDependencies,
config
)
}
const { items, registryNames } = await resolveDependenciesRecursively(
resolvedDependencies,
config,
options,
new Set(uniqueNames)
// Check for namespaced dependencies when no registries are configured
if (!config?.registries) {
const namespacedDeps = item.registryDependencies.filter((dep: string) =>
dep.startsWith("@")
)
allDependencyItems.push(...items)
allDependencyRegistryNames.push(...registryNames)
}
}
payload.push(...allDependencyItems)
// Handle any remaining registry names that need index resolution
if (allDependencyRegistryNames.length > 0) {
// Remove duplicates from registry names
const uniqueRegistryNames = Array.from(
new Set(allDependencyRegistryNames)
)
// Separate namespaced and non-namespaced items
const nonNamespacedItems = uniqueRegistryNames.filter(
(name) => !name.startsWith("@")
)
const namespacedDepItems = uniqueRegistryNames.filter((name) =>
name.startsWith("@")
)
// Handle namespaced dependency items
if (namespacedDepItems.length > 0) {
// This will now throw specific errors on failure
const depResults = await fetchRegistryItems(
namespacedDepItems,
config,
options
if (namespacedDeps.length > 0) {
const { registry } = parseRegistryAndItemFromString(namespacedDeps[0])
throw new RegistryNotConfiguredError(registry)
}
} else {
resolvedDependencies = resolveRegistryItemsFromRegistries(
item.registryDependencies,
config
)
for (let i = 0; i < depResults.length; i++) {
const item = depResults[i]
const itemWithSource: z.infer<typeof registryItemWithSourceSchema> = {
...item,
_source: namespacedDepItems[i],
}
payload.push(itemWithSource)
}
}
// For non-namespaced items, we need the index and style resolution
if (nonNamespacedItems.length > 0) {
const index = await getShadcnRegistryIndex()
if (!index && payload.length === 0) {
return null
}
if (index) {
// If we're resolving the index, we want it to go first
if (nonNamespacedItems.includes("index")) {
nonNamespacedItems.unshift("index")
}
// Resolve non-namespaced items through the existing flow
// Get URLs for all registry items including their dependencies
const registryUrls: string[] = []
for (const name of nonNamespacedItems) {
const itemDependencies = await resolveRegistryDependencies(
name,
config,
options
)
registryUrls.push(...itemDependencies)
}
// Deduplicate URLs
const uniqueUrls = Array.from(new Set(registryUrls))
let result = await fetchRegistry(uniqueUrls, options)
const registryPayload = z.array(registryItemSchema).parse(result)
payload.push(...registryPayload)
}
}
const { items, registryNames } = await resolveDependenciesRecursively(
resolvedDependencies,
config,
options,
new Set(uniqueNames)
)
allDependencyItems.push(...items)
allDependencyRegistryNames.push(...registryNames)
}
}
if (!payload.length) {
return null
}
payload.push(...allDependencyItems)
// No deduplication - we want to support multiple items with the same name from different sources
// Handle any remaining registry names that need index resolution
if (allDependencyRegistryNames.length > 0) {
// Remove duplicates from registry names
const uniqueRegistryNames = Array.from(new Set(allDependencyRegistryNames))
// If we're resolving the index, we want to fetch
// the theme item if a base color is provided.
// We do this for index only.
// Other components will ship with their theme tokens.
if (
uniqueNames.includes("index") ||
allDependencyRegistryNames.includes("index")
) {
if (config.tailwind.baseColor) {
const theme = await registryGetTheme(config.tailwind.baseColor, config)
if (theme) {
payload.unshift(theme)
}
}
}
// Build source map for topological sort
const sourceMap = new Map<z.infer<typeof registryItemSchema>, string>()
payload.forEach((item) => {
// Use the _source property if it was added, otherwise use the name
const source = item._source || item.name
sourceMap.set(item, source)
})
// Apply topological sort to ensure dependencies come before dependents
payload = topologicalSortRegistryItems(payload, sourceMap)
// Sort the payload so that registry:theme items come first,
// while maintaining the relative order of all items.
payload.sort((a, b) => {
if (a.type === "registry:theme" && b.type !== "registry:theme") {
return -1
}
if (a.type !== "registry:theme" && b.type === "registry:theme") {
return 1
}
return 0
})
let tailwind = {}
payload.forEach((item) => {
tailwind = deepmerge(tailwind, item.tailwind ?? {})
})
let cssVars = {}
payload.forEach((item) => {
cssVars = deepmerge(cssVars, item.cssVars ?? {})
})
let css = {}
payload.forEach((item) => {
css = deepmerge(css, item.css ?? {})
})
let docs = ""
payload.forEach((item) => {
if (item.docs) {
docs += `${item.docs}\n`
}
})
let envVars = {}
payload.forEach((item) => {
envVars = deepmerge(envVars, item.envVars ?? {})
})
// Deduplicate files based on resolved target paths.
const deduplicatedFiles = await deduplicateFilesByTarget(
payload.map((item) => item.files ?? []),
config
// Separate namespaced and non-namespaced items
const nonNamespacedItems = uniqueRegistryNames.filter(
(name) => !name.startsWith("@")
)
const namespacedDepItems = uniqueRegistryNames.filter((name) =>
name.startsWith("@")
)
const parsed = registryResolvedItemsTreeSchema.parse({
dependencies: deepmerge.all(
payload.map((item) => item.dependencies ?? [])
),
devDependencies: deepmerge.all(
payload.map((item) => item.devDependencies ?? [])
),
files: deduplicatedFiles,
tailwind,
cssVars,
css,
docs,
})
// Handle namespaced dependency items
if (namespacedDepItems.length > 0) {
// This will now throw specific errors on failure
const depResults = await fetchRegistryItems(
namespacedDepItems,
config,
options
)
if (Object.keys(envVars).length > 0) {
parsed.envVars = envVars
for (let i = 0; i < depResults.length; i++) {
const item = depResults[i]
const itemWithSource: z.infer<typeof registryItemWithSourceSchema> = {
...item,
_source: namespacedDepItems[i],
}
payload.push(itemWithSource)
}
}
return parsed
} catch (error) {
handleError(error)
// For non-namespaced items, we need the index and style resolution
if (nonNamespacedItems.length > 0) {
const index = await getShadcnRegistryIndex()
if (!index && payload.length === 0) {
return null
}
if (index) {
// If we're resolving the index, we want it to go first
if (nonNamespacedItems.includes("index")) {
nonNamespacedItems.unshift("index")
}
// Resolve non-namespaced items through the existing flow
// Get URLs for all registry items including their dependencies
const registryUrls: string[] = []
for (const name of nonNamespacedItems) {
const itemDependencies = await resolveRegistryDependencies(
name,
config,
options
)
registryUrls.push(...itemDependencies)
}
// Deduplicate URLs
const uniqueUrls = Array.from(new Set(registryUrls))
let result = await fetchRegistry(uniqueUrls, options)
const registryPayload = z.array(registryItemSchema).parse(result)
payload.push(...registryPayload)
}
}
}
if (!payload.length) {
return null
}
// No deduplication - we want to support multiple items with the same name from different sources
// If we're resolving the index, we want to fetch
// the theme item if a base color is provided.
// We do this for index only.
// Other components will ship with their theme tokens.
if (
uniqueNames.includes("index") ||
allDependencyRegistryNames.includes("index")
) {
if (config.tailwind.baseColor) {
const theme = await registryGetTheme(config.tailwind.baseColor, config)
if (theme) {
payload.unshift(theme)
}
}
}
// Build source map for topological sort
const sourceMap = new Map<z.infer<typeof registryItemSchema>, string>()
payload.forEach((item) => {
// Use the _source property if it was added, otherwise use the name
const source = item._source || item.name
sourceMap.set(item, source)
})
// Apply topological sort to ensure dependencies come before dependents
payload = topologicalSortRegistryItems(payload, sourceMap)
// Sort the payload so that registry:theme items come first,
// while maintaining the relative order of all items.
payload.sort((a, b) => {
if (a.type === "registry:theme" && b.type !== "registry:theme") {
return -1
}
if (a.type !== "registry:theme" && b.type === "registry:theme") {
return 1
}
return 0
})
let tailwind = {}
payload.forEach((item) => {
tailwind = deepmerge(tailwind, item.tailwind ?? {})
})
let cssVars = {}
payload.forEach((item) => {
cssVars = deepmerge(cssVars, item.cssVars ?? {})
})
let css = {}
payload.forEach((item) => {
css = deepmerge(css, item.css ?? {})
})
let docs = ""
payload.forEach((item) => {
if (item.docs) {
docs += `${item.docs}\n`
}
})
let envVars = {}
payload.forEach((item) => {
envVars = deepmerge(envVars, item.envVars ?? {})
})
// Deduplicate files based on resolved target paths.
const deduplicatedFiles = await deduplicateFilesByTarget(
payload.map((item) => item.files ?? []),
config
)
const parsed = registryResolvedItemsTreeSchema.parse({
dependencies: deepmerge.all(payload.map((item) => item.dependencies ?? [])),
devDependencies: deepmerge.all(
payload.map((item) => item.devDependencies ?? [])
),
files: deduplicatedFiles,
tailwind,
cssVars,
css,
docs,
})
if (Object.keys(envVars).length > 0) {
parsed.envVars = envVars
}
return parsed
}
async function resolveDependenciesRecursively(

94
pnpm-lock.yaml generated
View File

@@ -713,12 +713,18 @@ importers:
'@babel/plugin-transform-typescript':
specifier: ^7.28.0
version: 7.28.0(@babel/core@7.28.0)
'@babel/preset-typescript':
specifier: ^7.27.1
version: 7.27.1(@babel/core@7.28.0)
'@dotenvx/dotenvx':
specifier: ^1.48.4
version: 1.48.4
'@modelcontextprotocol/sdk':
specifier: ^1.17.2
version: 1.17.2
browserslist:
specifier: ^4.26.2
version: 4.26.2
commander:
specifier: ^14.0.0
version: 14.0.0
@@ -943,18 +949,36 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/plugin-syntax-jsx@7.27.1':
resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-typescript@7.27.1':
resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-transform-modules-commonjs@7.27.1':
resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-transform-typescript@7.28.0':
resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/preset-typescript@7.27.1':
resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.28.2':
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
engines: {node: '>=6.9.0'}
@@ -4030,6 +4054,10 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.8.4:
resolution: {integrity: sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==}
hasBin: true
basic-ftp@5.0.5:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'}
@@ -4062,8 +4090,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.25.2:
resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==}
browserslist@4.26.2:
resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -4138,6 +4166,9 @@ packages:
caniuse-lite@1.0.30001734:
resolution: {integrity: sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==}
caniuse-lite@1.0.30001743:
resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -4697,8 +4728,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.199:
resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==}
electron-to-chromium@1.5.218:
resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==}
embla-carousel-autoplay@8.0.0-rc15:
resolution: {integrity: sha512-ABTbDJGNb9jzI9OV2vSpbUvxUA0ELmK0SI3yPm8Haj3ghssS+vElfahoDqp7zuFkWBRih6w3B51oMPKdF5J55A==}
@@ -6860,8 +6891,8 @@ packages:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
node-releases@2.0.21:
resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
normalize-package-data@2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
@@ -9041,7 +9072,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.28.0
'@babel/helper-validator-option': 7.27.1
browserslist: 4.25.2
browserslist: 4.26.2
lru-cache: 5.1.1
semver: 6.3.1
@@ -9120,11 +9151,24 @@ snapshots:
dependencies:
'@babel/types': 7.28.2
'@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
'@babel/helper-plugin-utils': 7.27.1
'@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
'@babel/helper-plugin-utils': 7.27.1
'@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
'@babel/helper-plugin-utils': 7.27.1
transitivePeerDependencies:
- supports-color
'@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
@@ -9136,6 +9180,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/preset-typescript@7.27.1(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
'@babel/helper-plugin-utils': 7.27.1
'@babel/helper-validator-option': 7.27.1
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
'@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.0)
'@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
transitivePeerDependencies:
- supports-color
'@babel/runtime@7.28.2': {}
'@babel/template@7.27.2':
@@ -12962,7 +13017,7 @@ snapshots:
autoprefixer@10.4.21(postcss@8.5.6):
dependencies:
browserslist: 4.25.2
browserslist: 4.26.2
caniuse-lite: 1.0.30001734
fraction.js: 4.3.7
normalize-range: 0.1.2
@@ -13019,6 +13074,8 @@ snapshots:
base64-js@1.5.1: {}
baseline-browser-mapping@2.8.4: {}
basic-ftp@5.0.5: {}
better-path-resolve@1.0.0:
@@ -13062,12 +13119,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.25.2:
browserslist@4.26.2:
dependencies:
caniuse-lite: 1.0.30001734
electron-to-chromium: 1.5.199
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.2)
baseline-browser-mapping: 2.8.4
caniuse-lite: 1.0.30001743
electron-to-chromium: 1.5.218
node-releases: 2.0.21
update-browserslist-db: 1.1.3(browserslist@4.26.2)
buffer-crc32@0.2.13: {}
@@ -13141,6 +13199,8 @@ snapshots:
caniuse-lite@1.0.30001734: {}
caniuse-lite@1.0.30001743: {}
ccount@2.0.1: {}
chai@5.2.1:
@@ -13660,7 +13720,7 @@ snapshots:
ee-first@1.1.1: {}
electron-to-chromium@1.5.199: {}
electron-to-chromium@1.5.218: {}
embla-carousel-autoplay@8.0.0-rc15(embla-carousel@8.0.0-rc15):
dependencies:
@@ -16674,7 +16734,7 @@ snapshots:
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
node-releases@2.0.19: {}
node-releases@2.0.21: {}
normalize-package-data@2.5.0:
dependencies:
@@ -18926,9 +18986,9 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
'@unrs/resolver-binding-win32-x64-msvc': 1.11.1
update-browserslist-db@1.1.3(browserslist@4.25.2):
update-browserslist-db@1.1.3(browserslist@4.26.2):
dependencies:
browserslist: 4.25.2
browserslist: 4.26.2
escalade: 3.2.0
picocolors: 1.1.1