From fa8bf829961a2196a2e6cac3f33ebb5dbe7cdf4b Mon Sep 17 00:00:00 2001 From: ice breaker <1324318532@qq.com> Date: Sun, 11 Oct 2026 19:02:33 +0800 Subject: [PATCH 1/2] fix: tokenize rule-based unit conversions safely --- .changeset/fluffy-bears-serve.md | 5 + .changeset/mean-snakes-tease.md | 5 + apps/website/src/guide/recipes.md | 13 + apps/website/src/zh-CN/guide/recipes.md | 12 + packages/postcss-plugin-shared/README.md | 40 +- .../postcss-plugin-shared/README.zh-CN.md | 35 +- packages/postcss-plugin-shared/src/numbers.ts | 12 +- packages/postcss-plugin-shared/src/postcss.ts | 73 +- .../test-d/index.test-d.ts | 27 + .../postcss-plugin-shared/test/index.test.ts | 23 + .../test/value-transformer.test.ts | 246 +++++ packages/postcss-pxtrans/src/index.ts | 8 +- .../postcss-pxtrans/test/lifecycle.test.ts | 12 + .../src/index.ts | 12 +- .../test/lifecycle.test.ts | 11 + .../postcss-rem-to-viewport/src/plugin.ts | 11 +- .../test/lifecycle.test.ts | 12 + packages/postcss-rule-unit-converter/API.md | 70 +- .../postcss-rule-unit-converter/API.zh-CN.md | 62 +- .../postcss-rule-unit-converter/COOKBOOK.md | 23 +- .../COOKBOOK.zh-CN.md | 21 +- .../postcss-rule-unit-converter/MIGRATION.md | 12 +- .../MIGRATION.zh-CN.md | 10 +- .../PRESET_AUTHORING.md | 10 + .../PRESET_AUTHORING.zh-CN.md | 8 + .../postcss-rule-unit-converter/README.md | 44 +- .../README.zh-CN.md | 34 +- .../postcss-rule-unit-converter/package.json | 4 + .../postcss-rule-unit-converter/src/plugin.ts | 81 +- .../src/tokenize.ts | 60 ++ .../postcss-rule-unit-converter/src/types.ts | 18 +- .../test-d/index.test-d.ts | 18 + .../test/index.test.ts | 28 + .../test/tokenize.test.ts | 246 +++++ packages/postcss-units-to-px/src/plugin.ts | 7 +- .../test/lifecycle.test.ts | 12 + pnpm-lock.yaml | 901 +++++++++--------- scripts/check-cjs-plugins.mjs | 27 +- 38 files changed, 1633 insertions(+), 620 deletions(-) create mode 100644 .changeset/fluffy-bears-serve.md create mode 100644 .changeset/mean-snakes-tease.md create mode 100644 packages/postcss-plugin-shared/test/value-transformer.test.ts create mode 100644 packages/postcss-pxtrans/test/lifecycle.test.ts create mode 100644 packages/postcss-rem-to-responsive-pixel/test/lifecycle.test.ts create mode 100644 packages/postcss-rem-to-viewport/test/lifecycle.test.ts create mode 100644 packages/postcss-rule-unit-converter/src/tokenize.ts create mode 100644 packages/postcss-rule-unit-converter/test/tokenize.test.ts create mode 100644 packages/postcss-units-to-px/test/lifecycle.test.ts diff --git a/.changeset/fluffy-bears-serve.md b/.changeset/fluffy-bears-serve.md new file mode 100644 index 0000000..b23474a --- /dev/null +++ b/.changeset/fluffy-bears-serve.md @@ -0,0 +1,5 @@ +--- +"postcss-plugin-shared": minor +--- + +Add whole-value transforms to the shared walker and preserve important fallback declarations. diff --git a/.changeset/mean-snakes-tease.md b/.changeset/mean-snakes-tease.md new file mode 100644 index 0000000..d871eae --- /dev/null +++ b/.changeset/mean-snakes-tease.md @@ -0,0 +1,5 @@ +--- +"postcss-rule-unit-converter": patch +--- + +Parse complete CSS unit tokens safely, preserve signed and scientific values, and consistently skip var() contents. diff --git a/apps/website/src/guide/recipes.md b/apps/website/src/guide/recipes.md index 51025e9..a243ce4 100644 --- a/apps/website/src/guide/recipes.md +++ b/apps/website/src/guide/recipes.md @@ -15,3 +15,16 @@ The focused packages remain useful when their old API is part of your public bui ## Protecting declarations Use `propList` for property-level control, `selectorBlackList` for selectors, and `exclude` for file-level decisions. The shared matcher behavior is documented in `postcss-plugin-shared` and is consistent across the conversion packages. + +## Rule-engine matching boundaries + +`postcss-rule-unit-converter` uses CSS tokens by default: signed/scientific values +and complete decoded units are supported, while strings, URLs, identifiers, and +the whole `var()` fallback remain untouched. An explicit `unitRegex` replaces +that protection. The first matching rule wins even when it returns `undefined`, +and conversions do not chain in one run; repeated processing may convert again. +See the [API reference](/plugins/postcss-rule-unit-converter/api#matching-boundaries). + +Its `exclude` predicate and callback input refer to the root CSS entry. After +imports are expanded, inspect `context.decl?.source?.input.file` in a transform +for per-source decisions; `exclude` does not skip individual imported files. diff --git a/apps/website/src/zh-CN/guide/recipes.md b/apps/website/src/zh-CN/guide/recipes.md index 072a7cb..e5e4863 100644 --- a/apps/website/src/zh-CN/guide/recipes.md +++ b/apps/website/src/zh-CN/guide/recipes.md @@ -15,3 +15,15 @@ Tailwind 输出 `rem` 时,将 `postcss-rem-to-responsive-pixel` 放在 Tailwin ## 保护声明 用 `propList` 控制属性,用 `selectorBlackList` 控制选择器,用 `exclude` 控制文件。`postcss-plugin-shared` 中的 matcher 规则在各个转换包之间保持一致。 + +## 规则引擎的匹配边界 + +`postcss-rule-unit-converter` 默认使用 CSS token,支持带符号数值、科学计数法和 +完整解码后的单位,保护字符串、URL、标识符及整个 `var()` fallback。 +显式 `unitRegex` 会替代这些保护。第一条命中规则即为最终选择,即使它返回 +`undefined`;单轮不连锁转换,重复处理则可能继续转换。详见 +[API 参考](/zh-CN/plugins/postcss-rule-unit-converter/api#匹配边界)。 + +它的 `exclude` predicate 与回调 input 指向根 CSS 入口。展开 import 后,需要 +按原文件处理时,在 transform 中读取 `context.decl?.source?.input.file`; +`exclude` 不会单独跳过被导入的文件。 diff --git a/packages/postcss-plugin-shared/README.md b/packages/postcss-plugin-shared/README.md index a80e02b..188625c 100644 --- a/packages/postcss-plugin-shared/README.md +++ b/packages/postcss-plugin-shared/README.md @@ -52,7 +52,7 @@ Entry: `packages/postcss-plugin-shared/src/index.ts` - `createAdvancedPropListMatcher`: advanced prop matcher (wildcards + negation) for `string[]` - `createExcludeMatcher`: builds an exclude matcher from `exclude` (array or function) - `createSelectorBlacklistMatcher`: selector blacklist matcher with optional cache -- `declarationExists`: checks whether a rule/decls already contains the same `prop/value` to avoid duplicates +- `declarationExists`: checks whether a rule/decls already contains the same `prop/value`, optionally also importance, to avoid duplicates - `walkAndReplaceValues`: shared walker to replace declaration values and media params ## API @@ -221,15 +221,45 @@ isExcluded('/a/node_modules/x.css') // true isExcluded('/a/src/vendor.css') // true ``` -### `declarationExists(decls, prop, value)` +### `declarationExists(decls, prop, value, important?)` -Checks whether a rule already contains the same declaration (commonly used to avoid duplicates when `replace: false` and `cloneAfter` is used). +Checks whether a rule already contains the same property and value (commonly used to avoid duplicates when `replace: false` and `cloneAfter` is used). Omit `important` to match either priority, preserving the existing three-argument behavior. Pass `true` or `false` to match that exact normalized `!important` flag. ### `walkAndReplaceValues(options)` With the default `replace: true`, every matched declaration is converted in place, even if the same property and converted value already exist. Declaration order and `!important` are preserved; this function does not remove duplicate declarations. -`skipDuplicate` defaults to `true` and only controls insertion when `replace: false`: it prevents adding an existing property/value pair while retaining the source declaration. Set `skipDuplicate: false` to insert the converted declaration even when that pair already exists. +`skipDuplicate` defaults to `true` and only controls insertion when `replace: false`: it prevents adding an existing property/value/importance combination while retaining the source declaration. A normal declaration does not suppress an important fallback, or vice versa. Set `skipDuplicate: false` to insert the converted declaration even when that combination already exists. + +Choose one value-conversion strategy: + +```ts +interface WalkAndReplaceOptions { + // Optional context for source-less programmatic ASTs: + input?: Input + // Existing regex strategy: + unitRegex?: RegExp + createReplacer?: (context: ReplaceContext) => (m: string, $1?: string) => string + // Whole-value strategy; takes precedence when provided: + createValueTransformer?: (context: ReplaceContext) => (value: string) => string + // Other walker options are unchanged. +} +``` + +`createValueTransformer` bypasses regex matching and its precheck completely. It +receives each eligible declaration value, and media parameters when `mediaQuery` +is enabled. Filters, context, replacement/fallback handling, and deduplication are +shared by both strategies. Without a whole-value transformer, both `unitRegex` and +`createReplacer` are required; an incomplete strategy throws `TypeError`. + +`context.input`, `context.filePath`, and `exclude` use the root CSS entry. A +transformer can inspect `context.decl?.source?.input.file` to identify an imported +declaration's original file. + +For a programmatic root without `root.source.input`, pass an optional `input` to +provide callback and exclusion context. The real root input takes precedence. +The walker does not attach the supplied input to any AST source or create source +map metadata; without either input it leaves the root unchanged. `decls` only needs a `.some(...)` method that iterates PostCSS `ChildNode`s (usually a `Rule`). @@ -237,7 +267,7 @@ With the default `replace: true`, every matched declaration is converted in plac import { declarationExists } from 'postcss-plugin-shared' // inside PostCSS visitor -if (!declarationExists(rule, decl.prop, nextValue)) { +if (!declarationExists(rule, decl.prop, nextValue, Boolean(decl.important))) { decl.cloneAfter({ value: nextValue }) } ``` diff --git a/packages/postcss-plugin-shared/README.zh-CN.md b/packages/postcss-plugin-shared/README.zh-CN.md index ed6bced..39cf989 100644 --- a/packages/postcss-plugin-shared/README.zh-CN.md +++ b/packages/postcss-plugin-shared/README.zh-CN.md @@ -52,7 +52,7 @@ pnpm add postcss-plugin-shared - `createAdvancedPropListMatcher`:支持高级 `propList` 语法的属性匹配器(string[],通配符 + 取反) - `createExcludeMatcher`:根据 exclude 生成文件路径排除函数(数组或函数) - `createSelectorBlacklistMatcher`:选择器黑名单匹配器(支持缓存) -- `declarationExists`:用于检测某 rule/decls 中是否已存在相同 `prop/value`,避免重复注入 +- `declarationExists`:用于检测某 rule/decls 中是否已存在相同 `prop/value`,可同时比较优先级,避免重复注入 - `walkAndReplaceValues`:通用遍历替换逻辑(decl 值与 media params) ## API 说明 @@ -163,12 +163,39 @@ const re = createUnitRegex({ units: ['px', 'rpx'], ignoreCase: true }) 生成一个 `(filepath?: string) => boolean` 的排除函数。 -### `declarationExists(decls, prop, value)` +### `declarationExists(decls, prop, value, important?)` -用于避免在同一个规则中生成重复声明(常用于 `replace: false` 时 `cloneAfter` 的去重判断)。 +检查同一规则中是否已有相同属性和值(常用于 `replace: false` 时 `cloneAfter` 的去重判断)。省略 `important` 时匹配任意优先级,保持原有三参数行为;显式传入 `true` 或 `false` 时,严格匹配归一化后的 `!important` 标志。 ### `walkAndReplaceValues(options)` 默认 `replace: true` 会原位转换每条命中的声明,即使已存在相同属性和转换后值。声明顺序和 `!important` 保持不变;此函数不删除重复声明。 -`skipDuplicate` 默认为 `true`,仅控制 `replace: false` 时的插入行为:保留原声明,已有相同属性和值时不再插入。设置 `skipDuplicate: false` 后,即使已存在相同属性和值,仍插入转换后的声明。 +`skipDuplicate` 默认为 `true`,仅控制 `replace: false` 时的插入行为:保留原声明,已有相同属性、值和优先级时不再插入。普通声明不会阻止重要声明的 fallback,反之亦然。设置 `skipDuplicate: false` 后,即使存在相同组合,仍插入转换后的声明。 + +可选择两种值转换策略: + +```ts +interface WalkAndReplaceOptions { + // 为没有源码信息的手工 AST 提供可选上下文: + input?: Input + // 原有正则策略: + unitRegex?: RegExp + createReplacer?: (context: ReplaceContext) => (m: string, $1?: string) => string + // 整值转换策略;提供时优先使用: + createValueTransformer?: (context: ReplaceContext) => (value: string) => string + // 其他 walker 选项保持不变。 +} +``` + +`createValueTransformer` 完全绕过正则匹配及其预检查,接收每条符合条件的声明值; +开启 `mediaQuery` 后,也接收媒体查询参数。两种策略共享过滤、上下文、原位替换、 +fallback 和去重逻辑。未提供整值转换器时,必须同时提供 `unitRegex` 和 +`createReplacer`;不完整的策略会抛出 `TypeError`。 + +`context.input`、`context.filePath` 和 `exclude` 使用根 CSS 入口。转换器可通过 +`context.decl?.source?.input.file` 获取导入声明的原文件。 + +手工创建的根节点没有 `root.source.input` 时,可传入 `input` 作为回调和排除 +规则的上下文;真实根输入始终优先。walker 不会将备用输入附加到任何 AST +源码或创建 sourcemap 信息。两种输入都没有时,根节点保持原样。 diff --git a/packages/postcss-plugin-shared/src/numbers.ts b/packages/postcss-plugin-shared/src/numbers.ts index cfca471..c151d66 100644 --- a/packages/postcss-plugin-shared/src/numbers.ts +++ b/packages/postcss-plugin-shared/src/numbers.ts @@ -11,6 +11,16 @@ export function toFixed(number: number, precision: number) { const multiplier = 10 ** precision const sign = Math.sign(number) const absNumber = Math.abs(number) - const rounded = Math.round((absNumber + Number.EPSILON) * multiplier) / multiplier + // A fixed EPSILON can overwhelm very small lengths. Use a relative + // correction below that magnitude, preserving ordinary decimal rounding. + const correction = absNumber < Number.EPSILON ? Number.EPSILON * absNumber : Number.EPSILON + const correctedNumber = absNumber + correction + const scaledNumber = correctedNumber * multiplier + // Scaling large finite lengths can overflow even though no fractional + // digits remain to round at the requested precision. + if (Number.isFinite(number) && !Number.isFinite(scaledNumber)) { + return number + } + const rounded = Math.round(scaledNumber) / multiplier return sign * rounded } diff --git a/packages/postcss-plugin-shared/src/postcss.ts b/packages/postcss-plugin-shared/src/postcss.ts index 785b720..a0f6af2 100644 --- a/packages/postcss-plugin-shared/src/postcss.ts +++ b/packages/postcss-plugin-shared/src/postcss.ts @@ -14,6 +14,7 @@ import { /** * Check whether a declaration with the same property and value already exists. + * When supplied, `important` also requires the same importance flag. * * @example * if (declarationExists(rule, 'margin', '16px')) return @@ -22,6 +23,7 @@ export function declarationExists( decls: { some: (cb: (node: ChildNode) => boolean) => boolean }, prop: string, value: string, + important?: boolean, ) { const nodes = decls as unknown as { length?: number, [index: number]: ChildNode } const length = nodes.length @@ -34,7 +36,8 @@ export function declarationExists( } const decl = node as Declaration - if (decl.prop === prop && decl.value === value) { + if (decl.prop === prop && decl.value === value + && (important === undefined || Boolean(decl.important) === important)) { return true } } @@ -48,6 +51,7 @@ export function declarationExists( } const decl = node as Declaration return decl.prop === prop && decl.value === value + && (important === undefined || Boolean(decl.important) === important) }) } @@ -88,7 +92,10 @@ export interface ReplaceContext { */ export interface WalkAndReplaceOptions { root: Root - unitRegex: RegExp + /** Context for source-less ASTs. Existing root.source.input takes precedence. */ + input?: Input + /** Required with createReplacer unless createValueTransformer is supplied. */ + unitRegex?: RegExp propList: readonly (string | RegExp)[] selectorBlackList?: readonly (string | RegExp)[] exclude?: readonly (string | RegExp)[] | ((filePath: string) => boolean) @@ -96,7 +103,10 @@ export interface WalkAndReplaceOptions { /** 仅在 replace:false 时跳过已存在的转换结果;替换模式始终更新当前声明。 */ skipDuplicate?: boolean mediaQuery?: boolean - createReplacer: (context: ReplaceContext) => (m: string, $1?: string) => string + /** Required with unitRegex unless createValueTransformer is supplied. */ + createReplacer?: (context: ReplaceContext) => (m: string, $1?: string) => string + /** Transforms the complete value, taking precedence over the regex strategy. */ + createValueTransformer?: (context: ReplaceContext) => (value: string) => string shouldProcessDecl?: (decl: Declaration) => boolean shouldProcessAtRule?: (atRule: AtRule) => boolean } @@ -109,8 +119,8 @@ interface DeclarationIndex { counts: Map } -function declarationKey(prop: string, value: string) { - return `${prop}\0${value}` +function declarationKey(prop: string, value: string, important: boolean | undefined) { + return `${prop}\0${value}\0${important ? '1' : '0'}` } function createDeclarationIndex(container: DeclarationContainer): DeclarationIndex { @@ -119,19 +129,19 @@ function createDeclarationIndex(container: DeclarationContainer): DeclarationInd if (node.type !== 'decl') { continue } - const key = declarationKey(node.prop, node.value) + const key = declarationKey(node.prop, node.value, node.important) counts.set(key, (counts.get(key) ?? 0) + 1) } return { counts } } -function incrementDeclaration(index: DeclarationIndex, prop: string, value: string) { - const key = declarationKey(prop, value) +function incrementDeclaration(index: DeclarationIndex, prop: string, value: string, important: boolean | undefined) { + const key = declarationKey(prop, value, important) index.counts.set(key, (index.counts.get(key) ?? 0) + 1) } -function decrementDeclaration(index: DeclarationIndex, prop: string, value: string) { - const key = declarationKey(prop, value) +function decrementDeclaration(index: DeclarationIndex, prop: string, value: string, important: boolean | undefined) { + const key = declarationKey(prop, value, important) const count = index.counts.get(key) ?? 0 if (count <= 1) { index.counts.delete(key) @@ -141,8 +151,8 @@ function decrementDeclaration(index: DeclarationIndex, prop: string, value: stri } } -function hasDeclaration(index: DeclarationIndex, prop: string, value: string) { - return (index.counts.get(declarationKey(prop, value)) ?? 0) > 0 +function hasDeclaration(index: DeclarationIndex, prop: string, value: string, important: boolean | undefined) { + return (index.counts.get(declarationKey(prop, value, important)) ?? 0) > 0 } function createGlobalRegex(regex: RegExp) { @@ -177,12 +187,29 @@ export function walkAndReplaceValues(options: WalkAndReplaceOptions) { skipDuplicate = true, mediaQuery = false, createReplacer, + createValueTransformer, shouldProcessDecl, shouldProcessAtRule, } = options + let hasCandidate: (value: string) => boolean + let transformValue: (value: string, context: ReplaceContext) => string + if (createValueTransformer) { + hasCandidate = () => true + transformValue = (value, context) => createValueTransformer(context)(value) + } + else { + if (!unitRegex || !createReplacer) { + throw new TypeError('walkAndReplaceValues requires createValueTransformer or both unitRegex and createReplacer') + } + const replacementRegex = createGlobalRegex(unitRegex) + const unitTestRegex = new RegExp(unitRegex.source, unitRegex.flags.replace(/[gy]/g, '')) + hasCandidate = value => unitTestRegex.test(value) + transformValue = (value, context) => value.replace(replacementRegex, createReplacer(context)) + } + const source = root.source - const input = source?.input + const input = source?.input ?? options.input if (!input) { return } @@ -194,8 +221,6 @@ export function walkAndReplaceValues(options: WalkAndReplaceOptions) { } const satisfyPropList = createPropListMatcher(propList) - const replacementRegex = createGlobalRegex(unitRegex) - const unitTestRegex = new RegExp(unitRegex.source, unitRegex.flags.replace(/[gy]/g, '')) const hasSelectorBlackList = selectorBlackList.length > 0 const isBlacklisted = hasSelectorBlackList ? createSelectorBlacklistMatcher(selectorBlackList, { cache: true }) @@ -243,7 +268,7 @@ export function walkAndReplaceValues(options: WalkAndReplaceOptions) { if (shouldProcessDecl && !shouldProcessDecl(decl)) { return } - if (!unitTestRegex.test(decl.value)) { + if (!hasCandidate(decl.value)) { return } @@ -265,28 +290,27 @@ export function walkAndReplaceValues(options: WalkAndReplaceOptions) { prop: decl.prop, ...(atRule ? { atRule } : {}), } - const replacer = createReplacer(context) - const nextValue = decl.value.replace(replacementRegex, replacer) + const nextValue = transformValue(decl.value, context) if (nextValue === decl.value) { return } const index = getDeclarationIndex(parent ?? root) - if (!replace && skipDuplicate && index && hasDeclaration(index, decl.prop, nextValue)) { + if (!replace && skipDuplicate && index && hasDeclaration(index, decl.prop, nextValue, decl.important)) { return } if (replace) { if (index) { - decrementDeclaration(index, decl.prop, decl.value) - incrementDeclaration(index, decl.prop, nextValue) + decrementDeclaration(index, decl.prop, decl.value, decl.important) + incrementDeclaration(index, decl.prop, nextValue, decl.important) } decl.value = nextValue return } if (index) { - incrementDeclaration(index, decl.prop, nextValue) + incrementDeclaration(index, decl.prop, nextValue, decl.important) } const clone = decl.cloneAfter({ value: nextValue }) generatedDeclarations.add(clone) @@ -295,15 +319,14 @@ export function walkAndReplaceValues(options: WalkAndReplaceOptions) { if (mediaQuery && node.type === 'atrule') { const atRule = node as AtRule - if (!shouldProcess(atRule) || !unitTestRegex.test(atRule.params)) { + if (!shouldProcess(atRule) || !hasCandidate(atRule.params)) { return } const context: ReplaceContext = { ...baseContext, atRule, } - const replacer = createReplacer(context) - const nextParams = atRule.params.replace(replacementRegex, replacer) + const nextParams = transformValue(atRule.params, context) if (nextParams !== atRule.params) { atRule.params = nextParams } diff --git a/packages/postcss-plugin-shared/test-d/index.test-d.ts b/packages/postcss-plugin-shared/test-d/index.test-d.ts index 3a762b0..d3f459c 100644 --- a/packages/postcss-plugin-shared/test-d/index.test-d.ts +++ b/packages/postcss-plugin-shared/test-d/index.test-d.ts @@ -1,10 +1,37 @@ import { expectType } from 'tsd' +import postcss from 'postcss' import { createPropListMatcher, createUnitRegex, + declarationExists, toFixed, + walkAndReplaceValues, } from '../dist/index.mjs' expectType(toFixed(1.25, 2)) expectType(createUnitRegex({ units: ['rem', 'px'] })) expectType(createPropListMatcher(['*'])('font-size')) + +const root = postcss.parse('.rule{width:1rem}') +expectType(walkAndReplaceValues({ + root, + propList: ['*'], + unitRegex: /(\d+)rem/g, + createReplacer: context => (match, value) => { + expectType(context.filePath) + expectType(match) + expectType(value) + return '16px' + }, +})) +expectType(walkAndReplaceValues({ + root, + propList: ['*'], + createValueTransformer: context => (value) => { + expectType(context.decl) + expectType(value) + return value + }, +})) +expectType(declarationExists([], 'width', '16px')) +expectType(declarationExists([], 'width', '16px', true)) diff --git a/packages/postcss-plugin-shared/test/index.test.ts b/packages/postcss-plugin-shared/test/index.test.ts index 57ea871..d257d5f 100644 --- a/packages/postcss-plugin-shared/test/index.test.ts +++ b/packages/postcss-plugin-shared/test/index.test.ts @@ -27,6 +27,29 @@ describe('postcss-plugin-shared', () => { expect(toFixed(0.12345, 2)).toBe(0.12) expect(toFixed(0.1299, 2)).toBe(0.13) expect(toFixed(-0.1299, 2)).toBe(-0.13) + expect(toFixed(1.005, 2)).toBe(1.01) + expect(toFixed(-1.005, 2)).toBe(-1.01) + }) + + it.each([Number.MAX_VALUE, -Number.MAX_VALUE, 1e308, -1e308])('preserves finite values whose rounding scale overflows: %s', (value) => { + expect(toFixed(value, 5)).toBe(value) + expect(toFixed(value, 100)).toBe(value) + expect(toFixed(value, 0)).toBe(value) + }) + + it('does not add phantom precision to very small signed values', () => { + expect(toFixed(1e-100, 100)).toBe(1e-100) + expect(toFixed(-1e-100, 100)).toBe(-1e-100) + expect(toFixed(1e-308, 100)).toBe(0) + expect(toFixed(-1e-308, 100)).toBe(-0) + expect(toFixed(Number.MIN_VALUE, 5)).toBe(0) + expect(toFixed(-Number.MIN_VALUE, 5)).toBe(-0) + }) + + it('preserves nonfinite numeric semantics', () => { + expect(toFixed(Number.POSITIVE_INFINITY, 2)).toBe(Number.POSITIVE_INFINITY) + expect(toFixed(Number.NEGATIVE_INFINITY, 2)).toBe(Number.NEGATIVE_INFINITY) + expect(toFixed(Number.NaN, 2)).toBeNaN() }) }) diff --git a/packages/postcss-plugin-shared/test/value-transformer.test.ts b/packages/postcss-plugin-shared/test/value-transformer.test.ts new file mode 100644 index 0000000..accd240 --- /dev/null +++ b/packages/postcss-plugin-shared/test/value-transformer.test.ts @@ -0,0 +1,246 @@ +import type { ReplaceContext } from '../src/index' +import postcss from 'postcss' +import { declarationExists, walkAndReplaceValues } from '../src/index' + +describe('whole-value transformation', () => { + it('works without a regex and preserves declaration and media context', () => { + const root = postcss.parse('@media (min-width: token()) {.rule{width:token()}}', { from: '/src/entry.css' }) + const contexts: ReplaceContext[] = [] + const values: string[] = [] + walkAndReplaceValues({ + root, + propList: ['*'], + mediaQuery: true, + createValueTransformer: (context) => { + contexts.push(context) + return (value) => { + values.push(value) + return value.replace('token()', '16px') + } + }, + }) + + expect(root.toString()).toBe('@media (min-width: 16px) {.rule{width:16px}}') + expect(values).toEqual(['(min-width: token())', 'token()']) + expect(contexts[0]).toEqual({ root, input: root.source!.input, filePath: root.source!.input.file, atRule: root.first }) + expect(contexts[1]).toMatchObject({ + root, + input: root.source!.input, + filePath: root.source!.input.file, + atRule: root.first, + prop: 'width', + selector: '.rule', + }) + expect(contexts[1]!.decl?.value).toBe('16px') + expect(contexts[1]!.rule?.selector).toBe('.rule') + }) + + it('takes precedence over the regex and replacer without regex prefiltering', () => { + const root = postcss.parse('.rule{width:token()}') + const createReplacer = vi.fn(() => () => 'incorrect') + walkAndReplaceValues({ + root, + propList: ['*'], + unitRegex: /never-matches/, + createReplacer, + createValueTransformer: () => value => value.replace('token()', '32rpx'), + }) + expect(root.toString()).toBe('.rule{width:32rpx}') + expect(createReplacer).not.toHaveBeenCalled() + }) + + it('shares property, selector and custom declaration filters', () => { + const root = postcss.parse('.skip{width:token()}.keep{width:token();height:token();margin:token()}') + const seen: string[] = [] + walkAndReplaceValues({ + root, + propList: ['width', 'height'], + selectorBlackList: ['.skip'], + shouldProcessDecl: decl => decl.prop !== 'height', + createValueTransformer: (context) => { + seen.push(`${context.selector}:${context.prop}`) + return value => value.replace('token()', '16px') + }, + }) + expect(root.toString()).toBe('.skip{width:token()}.keep{width:16px;height:token();margin:token()}') + expect(seen).toEqual(['.keep:width']) + }) + + it('shares entry-level exclusions before constructing the transformer', () => { + const root = postcss.parse('.rule{width:token()}', { from: '/src/skip.css' }) + const createValueTransformer = vi.fn(() => () => '16px') + walkAndReplaceValues({ root, propList: ['*'], exclude: ['skip.css'], createValueTransformer }) + expect(root.toString()).toBe('.rule{width:token()}') + expect(createValueTransformer).not.toHaveBeenCalled() + }) + + it('keeps imported sources while exposing the root entry in the context', () => { + const root = postcss.parse('/* entry */', { from: '/src/entry.css' }) + const imported = postcss.parse('.imported{width:token()}', { from: '/src/imported.css' }) + root.append(imported) + const contexts: ReplaceContext[] = [] + walkAndReplaceValues({ + root, + propList: ['*'], + replace: false, + exclude: ['imported.css'], + createValueTransformer: (context) => { + contexts.push(context) + return value => value.replace('token()', '16px') + }, + }) + const rule = root.last as postcss.Rule + expect(contexts).toHaveLength(1) + expect(contexts[0]!.filePath).toBe(root.source!.input.file) + expect(contexts[0]!.decl?.source?.input.file).toBe(imported.source!.input.file) + expect(rule.last?.source).toBe(rule.first?.source) + expect(root.toResult({ map: { inline: false, annotation: false } }).map?.toJSON().sources) + .toEqual([ + expect.stringMatching(/(?:^|\/)src\/entry\.css$/), + expect.stringMatching(/(?:^|\/)src\/imported\.css$/), + ]) + }) + + it('allows custom at-rules and exposes declarations directly inside them', () => { + const root = postcss.parse('@supports (width:token()) {width:token()} @media (width:token()) {}') + const contexts: ReplaceContext[] = [] + walkAndReplaceValues({ + root, + propList: ['*'], + mediaQuery: true, + shouldProcessAtRule: atRule => atRule.name === 'supports', + createValueTransformer: (context) => { + contexts.push(context) + return value => value.replace('token()', '16px') + }, + }) + expect(root.toString()).toBe('@supports (width:16px) {width:16px} @media (width:token()) {}') + expect(contexts).toHaveLength(2) + expect(contexts[1]).toMatchObject({ atRule: root.first, prop: 'width' }) + expect(contexts[1]!.rule).toBeUndefined() + }) + + it('does not convert at-rule params unless mediaQuery is enabled', () => { + const root = postcss.parse('@media (width:token()) {.rule{width:token()}}') + walkAndReplaceValues({ + root, + propList: ['*'], + createValueTransformer: () => value => value.replace('token()', '16px'), + }) + expect(root.toString()).toBe('@media (width:token()) {.rule{width:16px}}') + }) + + it('does not recursively process inserted fallbacks in the same walk', () => { + const root = postcss.parse('.rule{width:1rem}') + const transform = vi.fn((value: string) => `${Number.parseFloat(value) * 2}rem`) + walkAndReplaceValues({ root, propList: ['*'], replace: false, createValueTransformer: () => transform }) + expect(root.toString()).toBe('.rule{width:1rem;width:2rem}') + expect(transform).toHaveBeenCalledTimes(1) + }) + + it('leaves declarations and media params untouched when values are unchanged', () => { + const root = postcss.parse('@media (width:16px) {.rule{width:16px}}') + const atRule = root.first as postcss.AtRule + const decl = (atRule.first as postcss.Rule).first + walkAndReplaceValues({ root, propList: ['*'], mediaQuery: true, createValueTransformer: () => value => value }) + expect(root.first).toBe(atRule) + expect((atRule.first as postcss.Rule).first).toBe(decl) + expect(root.toString()).toBe('@media (width:16px) {.rule{width:16px}}') + }) + + it('returns early for roots without source input', () => { + const createValueTransformer = vi.fn(() => () => '16px') + walkAndReplaceValues({ root: postcss.root(), propList: ['*'], createValueTransformer }) + expect(createValueTransformer).not.toHaveBeenCalled() + }) + + it('accepts fallback input for source-less ASTs without attaching source metadata', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: 'token()' })) + const input = new postcss.Input(root.toString(), { from: '/src/entry.css' }) + const contexts: ReplaceContext[] = [] + walkAndReplaceValues({ + root, + input, + propList: ['*'], + createValueTransformer(context) { + contexts.push(context) + return value => value.replace('token()', '16px') + }, + }) + expect(root.toString()).toContain('width: 16px') + expect(contexts[0]!.input).toBe(input) + expect(contexts[0]!.filePath).toBe(input.file) + expect(root.source).toBeUndefined() + expect(root.first!.source).toBeUndefined() + expect((root.first as postcss.Rule).first!.source).toBeUndefined() + }) + + it('prioritizes the real root input over a supplied fallback', () => { + const root = postcss.parse('.a{width:token()}', { from: '/src/real.css' }) + const contexts: ReplaceContext[] = [] + walkAndReplaceValues({ + root, + input: new postcss.Input('', { from: '/src/fallback.css' }), + propList: ['*'], + createValueTransformer(context) { + contexts.push(context) + return value => value.replace('token()', '16px') + }, + }) + expect(contexts[0]!.input).toBe(root.source!.input) + expect(contexts[0]!.filePath).toBe(root.source!.input.file) + }) + + it.each([ + {}, + { unitRegex: /rem/g }, + { createReplacer: () => (value: string) => value }, + ])('rejects an incomplete transformation strategy: %j', (strategy) => { + expect(() => walkAndReplaceValues({ root: postcss.parse('.rule{width:1rem}'), propList: ['*'], ...strategy })) + .toThrow('requires createValueTransformer or both unitRegex and createReplacer') + }) +}) + +describe('importance-aware declarations', () => { + it.each([true, false])('supports explicit importance in .some and array-like containers: %s', (important) => { + const decl = postcss.decl({ prop: 'width', value: '16px', important }) + const nodes = [postcss.comment({ text: 'non-declaration' }), decl] + const containers = [nodes, { some: (callback: (node: postcss.ChildNode) => boolean) => nodes.some(callback) }] + for (const container of containers) { + expect(declarationExists(container, 'width', '16px')).toBe(true) + expect(declarationExists(container, 'width', '16px', important)).toBe(true) + expect(declarationExists(container, 'width', '16px', !important)).toBe(false) + expect(declarationExists(container, 'height', '16px', important)).toBe(false) + } + }) + + it.each([ + ['16px;token()!important', '16px;token()!important;16px!important'], + ['16px!important;token()', '16px!important;token();16px'], + ['16px!important;token()!important', '16px!important;token()!important'], + ['token()!important;token()!important', 'token()!important;16px!important;token()!important'], + ['token();token()!important', 'token();16px;token()!important;16px!important'], + ])('deduplicates fallback by value and importance: %s', (input, expected) => { + const css = (values: string) => `.rule{${values.split(';').map(value => `width:${value}`).join(';')}}` + const root = postcss.parse(css(input)) + walkAndReplaceValues({ + root, + propList: ['*'], + replace: false, + createValueTransformer: () => value => value.replace('token()', '16px'), + }) + expect(root.toString()).toBe(css(expected)) + }) + + it('applies importance-aware fallback deduplication to legacy regex callers', () => { + const root = postcss.parse('.rule{width:16px;width:1rem!important}') + walkAndReplaceValues({ + root, + propList: ['*'], + replace: false, + unitRegex: /(\d+)rem/g, + createReplacer: () => (_match, value) => `${Number(value) * 16}px`, + }) + expect(root.toString()).toBe('.rule{width:16px;width:1rem!important;width:16px!important}') + }) +}) diff --git a/packages/postcss-pxtrans/src/index.ts b/packages/postcss-pxtrans/src/index.ts index 18c0079..1c1ab28 100644 --- a/packages/postcss-pxtrans/src/index.ts +++ b/packages/postcss-pxtrans/src/index.ts @@ -1,4 +1,4 @@ -import type { Input, Result, Root } from 'postcss' +import type { Helpers, Input, Plugin, Result, Root } from 'postcss' import type { ConversionRule } from 'postcss-rule-unit-converter' import type { PxTransformMethod, @@ -354,7 +354,7 @@ function plugin(userOptions: PxTransformOptions = {}) { rules, unitPrecision, unitRegex: pxRegex(transUnits), - }) as { Once?: (css: Root) => void } + }) as Plugin return { postcssPlugin, @@ -374,7 +374,7 @@ function plugin(userOptions: PxTransformOptions = {}) { : false return { - Once(css: Root) { + Once(css: Root, helpers: Helpers) { css.walkComments((comment) => { if (comment.text === 'postcss-pxtrans disable') { css.raws['__pxtransSkip'] = true @@ -387,7 +387,7 @@ function plugin(userOptions: PxTransformOptions = {}) { return } - wrappedPlugin.Once?.(css) + return wrappedPlugin.Once?.(css, helpers) }, } }, diff --git a/packages/postcss-pxtrans/test/lifecycle.test.ts b/packages/postcss-pxtrans/test/lifecycle.test.ts new file mode 100644 index 0000000..eda08a7 --- /dev/null +++ b/packages/postcss-pxtrans/test/lifecycle.test.ts @@ -0,0 +1,12 @@ +import postcss from 'postcss' +import pxtrans from '../src/index' + +it('forwards native PostCSS context through guarded pxtrans delegation for source-less AST input', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: '20px' })) + const result = postcss(pxtrans({ platform: 'weapp', designWidth: 750 })) + .process(root, { from: 'explicit.css' }) + .sync() + + expect(result.root.toString()).toContain('width: 20rpx') + expect(root.source).toBeUndefined() +}) diff --git a/packages/postcss-rem-to-responsive-pixel/src/index.ts b/packages/postcss-rem-to-responsive-pixel/src/index.ts index 55646ca..2926ea6 100644 --- a/packages/postcss-rem-to-responsive-pixel/src/index.ts +++ b/packages/postcss-rem-to-responsive-pixel/src/index.ts @@ -1,6 +1,6 @@ -import type { Root } from 'postcss' +import type { Plugin } from 'postcss' import type { PostcssRemToResponsivePixel, UserDefinedOptions } from './types' -import unitConverter, { presets } from 'postcss-rule-unit-converter' +import unitConverter, { createUnitRegex, presets } from 'postcss-rule-unit-converter' import { getConfig, postcssPlugin } from './shared' export * from './types' @@ -67,13 +67,13 @@ const plugin: PostcssRemToResponsivePixel = ( ], selectorBlackList, unitPrecision, - }) as { Once?: (css: Root) => void } + // Keep the wrapper's historical case-sensitive rem parsing. + unitRegex: createUnitRegex(['rem']), + }) as Plugin return { postcssPlugin, - [processorStage]: (css: Root) => { - wrappedPlugin.Once?.(css) - }, + [processorStage]: wrappedPlugin.Once, } } diff --git a/packages/postcss-rem-to-responsive-pixel/test/lifecycle.test.ts b/packages/postcss-rem-to-responsive-pixel/test/lifecycle.test.ts new file mode 100644 index 0000000..f4fdb30 --- /dev/null +++ b/packages/postcss-rem-to-responsive-pixel/test/lifecycle.test.ts @@ -0,0 +1,11 @@ +import postcss from 'postcss' +import remToResponsivePixel from '../src/index' + +it.each(['Once', 'OnceExit'] as const)('preserves native PostCSS context at %s for source-less AST input', (processorStage) => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'font-size', value: '2rem' })) + const processor = postcss(remToResponsivePixel({ processorStage, rootValue: input => input.file?.endsWith('explicit.css') ? 20 : 16 })) + const result = processor.process(root, { from: 'explicit.css' }).sync() + + expect(result.root.toString()).toContain('font-size: 40px') + expect(root.source).toBeUndefined() +}) diff --git a/packages/postcss-rem-to-viewport/src/plugin.ts b/packages/postcss-rem-to-viewport/src/plugin.ts index 206bb4f..6d67aff 100644 --- a/packages/postcss-rem-to-viewport/src/plugin.ts +++ b/packages/postcss-rem-to-viewport/src/plugin.ts @@ -1,5 +1,6 @@ +import type { Plugin } from 'postcss' import type { PostcssRemToViewport, UserDefinedOptions } from './types' -import unitConverter, { presets } from 'postcss-rule-unit-converter' +import unitConverter, { createUnitRegex, presets } from 'postcss-rule-unit-converter' import { getConfig, postcssPlugin } from './shared' /** @@ -62,13 +63,13 @@ const plugin: PostcssRemToViewport = ( ], selectorBlackList, unitPrecision, - }) as { Once?: (css: unknown) => void } + // Keep the wrapper's historical case-sensitive rem parsing. + unitRegex: createUnitRegex(['rem']), + }) as Plugin return { postcssPlugin, - Once(css) { - wrappedPlugin.Once?.(css) - }, + Once: wrappedPlugin.Once!, } } diff --git a/packages/postcss-rem-to-viewport/test/lifecycle.test.ts b/packages/postcss-rem-to-viewport/test/lifecycle.test.ts new file mode 100644 index 0000000..95cd8b2 --- /dev/null +++ b/packages/postcss-rem-to-viewport/test/lifecycle.test.ts @@ -0,0 +1,12 @@ +import postcss from 'postcss' +import remToViewport from '../src/index' + +it('preserves native PostCSS context for source-less AST input', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'font-size', value: '2rem' })) + const result = postcss(remToViewport({ rootValue: input => input.file?.endsWith('explicit.css') ? 320 : 375 })) + .process(root, { from: 'explicit.css' }) + .sync() + + expect(result.root.toString()).toContain('font-size: 10vw') + expect(root.source).toBeUndefined() +}) diff --git a/packages/postcss-rule-unit-converter/API.md b/packages/postcss-rule-unit-converter/API.md index 8602622..33541c5 100644 --- a/packages/postcss-rule-unit-converter/API.md +++ b/packages/postcss-rule-unit-converter/API.md @@ -35,16 +35,16 @@ interface UserDefinedOptions { ``` - `rules`: ordered conversion rules. First match wins. -- `unitRegex`: override the generated matching regex. +- `unitRegex`: opt out of default CSS tokenization and use a custom regex instead. - `unitPrecision`: decimal precision for generated values. -- `minValue`: global minimum source value to convert. +- `minValue`: global minimum absolute source magnitude to convert (default `0`). Negative lengths remain eligible. - `keepZeroUnit`: keep `0px`/`0rem` style output instead of collapsing to bare `0`. - `selectorBlackList`: skip matching selectors. - `propList`: restrict processing to matching properties. Supports negated string entries like `!font-size`, `!padding*`, and `!--wot-*-font-size`. -- `replace`: replace the original declaration instead of cloning a fallback declaration. +- `replace`: replace the original declaration instead of cloning a fallback declaration (default `true`). Fallback deduplication compares property, value, and `!important`. - `mediaQuery`: also convert matching units in `@media` params. -- `exclude`: skip files by path or predicate. +- `exclude`: skip the entire root entry by path or predicate (default `[/node_modules/i]`). It does not filter imported declarations by their original file. - `disabled`: no-op mode. ### Choosing Between `exclude`, `selectorBlackList`, and `propList` @@ -53,7 +53,7 @@ interface UserDefinedOptions { - `selectorBlackList`: skip matching selectors inside an otherwise processed file - `propList`: skip or include specific CSS properties inside matched selectors -Use `exclude` for file-level boundaries such as `node_modules` or generated CSS. +Use `exclude` for root-entry boundaries such as `node_modules` or generated CSS. Use `selectorBlackList` when a rule or component should be left untouched. Use `propList` when the file and selector should still be processed, but some properties such as `font-size` or `--wot-*-font-size` should be skipped. @@ -75,16 +75,20 @@ interface ConversionRule { - `from`: source unit matcher. - `to`: fallback output unit. - `factor`: shorthand numeric conversion. -- `minValue`: per-rule minimum source value override. +- `minValue`: per-rule minimum absolute source magnitude, overriding the global threshold. - `transform`: full callback when `factor` is not enough. -Rules are evaluated in array order. If multiple rules can match the same source unit, the first matching rule is used. +Rules are evaluated in array order. The first matching rule is final, including +when its `transform` returns `undefined` or its `minValue` threshold is not met. +Later rules do not retry that token. Each original token is converted once; a +`rem -> px` rule followed by a `px -> rpx` rule does not chain in one run. A second +processing run may convert the generated `px`, so configure one stage when a +single conversion is intended. -String unit matchers are trimmed and normalized to lowercase. With the default -generated regex, a string matcher such as `'px'` matches lowercase `px` in CSS. -Use a `RegExp` matcher such as `/^px$/i`, or a custom `unitRegex` that captures -uppercase units, when you need `PX`/`Px` style matches. `rawUnit` still exposes -the original casing. +String unit matchers are trimmed and normalized to lowercase. Default tokenization +decodes CSS unit escapes and normalizes unit case, so `'px'` also matches `PX`, +`Px`, and equivalent escaped spellings. Regex and function matchers receive that +normalized unit. `rawUnit` retains the original source text. ## Transform Callback @@ -102,7 +106,10 @@ interface ConvertedValue { - Return `number` to reuse `to` as the output unit. - Return `{ value, unit }` to override the output unit. -- Return `undefined` to leave the original match unchanged. +- Return `undefined` to leave the original match unchanged, without trying later rules. + +`value` is signed and supports scientific notation: `-2rem` passes `-2`, and +`1e3rem` passes `1000`. `minValue` compares `Math.abs(value)` before the callback. ## RuleContext @@ -125,20 +132,33 @@ interface RuleContext { } ``` -- `fromUnit`: source unit used for rule matching. It is normalized to lowercase - when the plugin uses a custom or broad unit regex. -- `rawUnit`: original matched unit text, preserving source casing such as `PX`. -- `rawValue`: original matched numeric text before `Number(...)`, such as `.5`. -- `match`: full matched fragment, such as `.5PX`. - -The default generated regex also contains skip alternatives for quoted strings, -`url(...)`, and `var(...)`. Those alternatives do not provide numeric/unit -capture groups, so the replacer returns the original text unchanged. If you pass -`unitRegex`, it replaces the generated regex entirely; include your own skip -alternatives if you still want strings, URLs, or CSS variables to remain untouched. +- `fromUnit`: lowercase source unit used for rule matching; default tokenization also decodes CSS escapes. +- `rawUnit`: original unit text, preserving casing such as `PX` and CSS escapes. +- `rawValue`: original signed numeric text before `Number(...)`, such as `-.5` or `1e3`. +- `match`: full matched fragment, such as `-.5PX`. +- `input` and `filePath`: root-entry input and path, including when other CSS files have been imported. Inspect `decl?.source?.input.file` for a declaration's original file; `decl` is absent for media-query parameters. + +Programmatic ASTs without root source metadata are supported. Their callback and +filter context uses an ephemeral `Input` containing the original AST text and +the explicit `process({ from })`, if supplied. This does not attach sources to +nodes or invent a filename for source-less input. + +## Matching Boundaries + +Without `unitRegex`, a CSS tokenizer visits complete dimension and percentage +tokens. A `rem` rule leaves `2remfoo` and `foo2rem` untouched. Signed values, +scientific notation, unit case, and CSS escapes follow CSS token boundaries. +Strings, comments, and URL contents remain untouched, including escaped quotes +and uppercase `URL()`. The whole `var()` function is skipped at every nesting +depth, including its fallback; lengths in other functions such as `calc()` and +`min()` are processed normally. + +An explicit `unitRegex` replaces tokenization entirely, including those protections. The expression must expose the numeric value as capture group 1 and the unit as capture group 2. A missing global flag is added automatically so every match in -a declaration is visited. +a declaration is visited. Captured units are lowercased for rule matching; the +custom regex is responsible for escapes, signed/scientific numbers, token +boundaries, and any string, URL, or `var()` skip alternatives. ## Helpers diff --git a/packages/postcss-rule-unit-converter/API.zh-CN.md b/packages/postcss-rule-unit-converter/API.zh-CN.md index 5b84112..f58bee5 100644 --- a/packages/postcss-rule-unit-converter/API.zh-CN.md +++ b/packages/postcss-rule-unit-converter/API.zh-CN.md @@ -35,16 +35,16 @@ interface UserDefinedOptions { ``` - `rules`:有序转换规则,先命中的先生效。 -- `unitRegex`:覆盖插件自动生成的匹配正则。 +- `unitRegex`:关闭默认 CSS tokenizer,改用自定义正则匹配。 - `unitPrecision`:输出值的小数精度。 -- `minValue`:全局最小转换阈值。 +- `minValue`:全局源值绝对值阈值,默认 `0`;负长度也可转换。 - `keepZeroUnit`:保留 `0px`、`0rem` 这类带单位的零值输出,而不是折叠成裸 `0`。 - `selectorBlackList`:命中这些选择器时跳过。 - `propList`:仅处理匹配的属性。支持 `!font-size`、`!padding*`、 `!--wot-*-font-size` 这类取反 / glob 写法。 -- `replace`:是否直接替换原声明;否则追加 fallback。 +- `replace`:是否直接替换原声明,默认 `true`;否则追加 fallback,按属性、值与 `!important` 去重。 - `mediaQuery`:是否处理 `@media` 参数中的单位。 -- `exclude`:按文件路径或 predicate 跳过整文件。 +- `exclude`:按路径或 predicate 跳过整个根入口,默认 `[/node_modules/i]`;不会按导入声明的原文件过滤。 - `disabled`:禁用插件。 ### `exclude`、`selectorBlackList`、`propList` 怎么选 @@ -53,7 +53,7 @@ interface UserDefinedOptions { - `selectorBlackList`:选择器级跳过,文件仍会继续处理 - `propList`:属性级控制,在命中的规则里决定哪些属性处理、哪些属性跳过 -适合用 `exclude` 的场景是 `node_modules`、生成产物、整类文件不想处理。 +适合用 `exclude` 的场景是 `node_modules`、生成产物、整类根入口不想处理。 适合用 `selectorBlackList` 的场景是某些组件或规则整体保留原样。适合用 `propList` 的场景是文件和选择器仍然需要处理,但像 `font-size` 或 `--wot-*-font-size` 这样的属性需要跳过。 @@ -75,15 +75,17 @@ interface ConversionRule { - `from`:源单位匹配器。 - `to`:默认输出单位。 - `factor`:简单倍率转换写法。 -- `minValue`:单条规则自己的最小转换阈值,会覆盖全局 `minValue`。 +- `minValue`:单条规则的源值绝对值阈值,会覆盖全局 `minValue`。 - `transform`:需要完整自定义逻辑时使用。 -规则按数组顺序执行。如果多条规则都能命中同一个源单位,只有第一条会生效。 +规则按数组顺序执行,第一条命中的规则即为最终选择。即使它的 `transform` +返回 `undefined`,或源值未达到它的 `minValue`,也不会改用后面的规则。 +每轮只转换原始 token 一次;`rem -> px` 后跟 `px -> rpx` 不会在同一轮连锁执行。 +再次处理时可能继续转换生成的 `px`,只想换算一次时应只配置一个转换阶段。 -字符串单位 matcher 会先 trim 并归一化为小写。使用默认自动生成的正则时, -`'px'` 这类字符串 matcher 匹配 CSS 中的小写 `px`。如果需要匹配 `PX`/`Px` -这类大小写形式,请使用 `/^px$/i` 这样的正则 matcher,或传入能捕获大写单位的 -自定义 `unitRegex`。原始大小写仍可通过 `rawUnit` 读取。 +字符串单位 matcher 会先 trim 并归一化为小写。默认 tokenizer 会解码 CSS 单位 +转义并归一化大小写,因此 `'px'` 也能匹配 `PX`、`Px` 及等价转义写法。 +正则与函数 matcher 接收到的也是归一化后的单位,`rawUnit` 保留原始文本。 ## transform 回调 @@ -101,7 +103,10 @@ interface ConvertedValue { - 返回 `number`:沿用 `to` 作为输出单位。 - 返回 `{ value, unit }`:同时自定义输出值和输出单位。 -- 返回 `undefined`:保持原始匹配结果不变。 +- 返回 `undefined`:保持原始匹配结果不变,不再尝试后续规则。 + +`value` 带有真实符号,并支持科学计数法:`-2rem` 传入 `-2`,`1e3rem` +传入 `1000`。调用回调前,`minValue` 按 `Math.abs(value)` 比较。 ## RuleContext @@ -124,17 +129,28 @@ interface RuleContext { } ``` -- `fromUnit`:用于规则匹配的源单位。插件使用自定义或宽泛单位正则时会归一化为小写。 -- `rawUnit`:原始匹配到的单位文本,会保留 `PX` 这类大小写。 -- `rawValue`:`Number(...)` 之前的原始数字文本,例如 `.5`。 -- `match`:完整匹配片段,例如 `.5PX`。 - -默认自动生成的正则还包含对字符串、`url(...)` 和 `var(...)` 的跳过分支。 -这些分支不会提供数字 / 单位捕获组,因此 replacer 会原样返回。传入 -`unitRegex` 时会完全替换默认正则;如果仍希望跳过字符串、URL 或 CSS 变量, -需要在自定义正则里保留对应跳过分支。正则必须把数字放在第 1 个捕获组、 -单位放在第 2 个捕获组;如果没有 `g` 标志,插件会自动补上,确保声明中的 -每个匹配项都会处理。 +- `fromUnit`:用于规则匹配的小写源单位;默认 tokenizer 还会解码 CSS 转义。 +- `rawUnit`:原始单位文本,保留 `PX` 这样的大小写与 CSS 转义。 +- `rawValue`:`Number(...)` 前带符号的数字文本,例如 `-.5` 或 `1e3`。 +- `match`:完整匹配片段,例如 `-.5PX`。 +- `input` 和 `filePath`:根入口的输入与路径,即使已展开其他 CSS 文件。声明的原文件可通过 `decl?.source?.input.file` 获取;处理媒体查询参数时没有 `decl`。 + +支持没有根源码信息的手工 AST。回调和过滤上下文使用临时 `Input`,包含转换前 +的 AST 文本及显式传入的 `process({ from })`。它不会给节点附加源码信息,也 +不会为没有路径的输入伪造文件名。 + +## 匹配边界 + +不配置 `unitRegex` 时,CSS tokenizer 遍历完整的 dimension 和百分比 token。 +`rem` 规则不会误改 `2remfoo` 或 `foo2rem`。符号、科学计数法、单位大小写和 +CSS 转义都按 CSS token 边界处理。字符串、注释与 URL 内容保持不变,包括 +转义引号和大写 `URL()`。默认完整跳过任意嵌套深度的 `var()` 及其 fallback; +`calc()`、`min()` 等其他函数中的长度正常转换。 + +显式 `unitRegex` 会完全替代 tokenizer 及上述保护。正则必须把数字放在第 1 个 +捕获组、单位放在第 2 个捕获组;缺少 `g` 标志时会自动补上,确保每个匹配项都会 +处理。捕获到的单位会转成小写用于规则匹配;转义、带符号数字、科学计数法、 +token 边界及字符串、URL、`var()` 跳过逻辑由自定义正则负责。 ## 辅助函数 diff --git a/packages/postcss-rule-unit-converter/COOKBOOK.md b/packages/postcss-rule-unit-converter/COOKBOOK.md index 80fbd95..3d359fe 100644 --- a/packages/postcss-rule-unit-converter/COOKBOOK.md +++ b/packages/postcss-rule-unit-converter/COOKBOOK.md @@ -96,8 +96,10 @@ export default { ``` This keeps the original declaration and appends the converted one after it. +Existing fallbacks are compared by property, value, and `!important`, so a normal +declaration does not suppress an important fallback. -## Dynamic Root Value By File +## Dynamic Root Value By Entry ```ts import unitConverter, { presets } from 'postcss-rule-unit-converter' @@ -118,7 +120,24 @@ export default { Use when: - mobile and tablet styles share one build -- different source files need different conversion baselines +- different root entries need different conversion baselines + +The preset resolver receives the root-entry `Input`. After an importer expands +several files, it does not switch input for each declaration. For per-source +conversion, use a rule callback and inspect the declaration source: + +```ts +unitConverter({ + rules: [{ + from: 'rem', + to: 'px', + transform(value, context) { + const source = context.decl?.source?.input.file ?? context.filePath + return value * (source?.includes('tablet') ? 18 : 16) + }, + }], +}) +``` ## Custom Preset In App Config diff --git a/packages/postcss-rule-unit-converter/COOKBOOK.zh-CN.md b/packages/postcss-rule-unit-converter/COOKBOOK.zh-CN.md index 83a186c..295e47a 100644 --- a/packages/postcss-rule-unit-converter/COOKBOOK.zh-CN.md +++ b/packages/postcss-rule-unit-converter/COOKBOOK.zh-CN.md @@ -96,8 +96,9 @@ export default { ``` 这个配置会保留原始声明,并把转换后的声明追加在后面。 +已有 fallback 按属性、值和 `!important` 比较,普通声明不会阻止重要声明的 fallback。 -## 按文件动态切换 Root Value +## 按根入口动态切换 Root Value ```ts import unitConverter, { presets } from 'postcss-rule-unit-converter' @@ -118,7 +119,23 @@ export default { 适用场景: - 手机和平板共用一套构建 -- 不同文件需要不同转换基准 +- 不同根入口需要不同转换基准 + +preset 的 resolver 接收根入口 `Input`。importer 将多个文件展开后,不会为每条 +声明切换 input。需要按原文件换算时,在规则回调中读取声明来源: + +```ts +unitConverter({ + rules: [{ + from: 'rem', + to: 'px', + transform(value, context) { + const source = context.decl?.source?.input.file ?? context.filePath + return value * (source?.includes('tablet') ? 18 : 16) + }, + }], +}) +``` ## 在业务里自定义 Preset diff --git a/packages/postcss-rule-unit-converter/MIGRATION.md b/packages/postcss-rule-unit-converter/MIGRATION.md index 320c85a..3d6e7ef 100644 --- a/packages/postcss-rule-unit-converter/MIGRATION.md +++ b/packages/postcss-rule-unit-converter/MIGRATION.md @@ -1,12 +1,22 @@ # Migration Guide -Move legacy packages to `postcss-rule-unit-converter` without changing behavior. +Move legacy packages to `postcss-rule-unit-converter`, preserving conversion formulas while reviewing the matching boundaries below. - Overview: [README.md](./README.md) - API: [API.md](./API.md) - Recipes: [COOKBOOK.md](./COOKBOOK.md) - Preset authoring: [PRESET_AUTHORING.md](./PRESET_AUTHORING.md) +## Matching Changes + +The rule engine uses CSS tokenization by default. It treats signed/scientific +numbers as complete values, matches case-insensitive decoded units, and protects +strings, URLs, identifiers, and the entire `var()` fallback at any depth. +Callbacks receive signed values, and `minValue` compares absolute magnitude. +If your old configuration relied on regex substring matches or converting inside +`var()`, review [matching boundaries](./API.md#matching-boundaries) and explicitly +configure `unitRegex` only when that behavior is intended. + ## When To Migrate Prefer `postcss-rule-unit-converter` when you need: diff --git a/packages/postcss-rule-unit-converter/MIGRATION.zh-CN.md b/packages/postcss-rule-unit-converter/MIGRATION.zh-CN.md index 90968b4..9afce10 100644 --- a/packages/postcss-rule-unit-converter/MIGRATION.zh-CN.md +++ b/packages/postcss-rule-unit-converter/MIGRATION.zh-CN.md @@ -1,12 +1,20 @@ # Migration Guide -把老包迁移到 `postcss-rule-unit-converter`,同时尽量保持行为不变。 +把老包迁移到 `postcss-rule-unit-converter`,保留换算公式,并检查下文的匹配边界变化。 - 总览与快速上手:[README.zh-CN.md](./README.zh-CN.md) - API 文档:[API.zh-CN.md](./API.zh-CN.md) - 场景示例:[COOKBOOK.zh-CN.md](./COOKBOOK.zh-CN.md) - Preset 编写指南:[PRESET_AUTHORING.zh-CN.md](./PRESET_AUTHORING.zh-CN.md) +## 匹配行为变化 + +规则引擎默认使用 CSS tokenizer,完整解析带符号数字与科学计数法,解码单位并按 +小写匹配,保护字符串、URL、标识符及任意深度的整个 `var()` fallback。 +回调收到带符号数值,`minValue` 按绝对值比较。旧配置若依赖正则子串匹配或 +转换 `var()` 中的内容,请查看 [匹配边界](./API.zh-CN.md#匹配边界),仅在确实 +需要时显式配置 `unitRegex`。 + ## 什么时候该迁移 如果你需要下面这些能力,更建议迁移到 `postcss-rule-unit-converter`: diff --git a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md index e006f00..908f735 100644 --- a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md +++ b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md @@ -145,6 +145,13 @@ const rule = { } ``` +Callbacks receive signed values, including scientific notation. Do not add a +second sign to negative inputs. `minValue` is an absolute-magnitude threshold. +`context.input` and `context.filePath` identify the root entry; use +`context.decl?.source?.input.file` when an imported declaration's origin matters. +See [matching boundaries](./API.md#matching-boundaries) for the default tokenizer +and the explicit `unitRegex` escape hatch. + ## File Organization Recommended package layout: @@ -199,6 +206,9 @@ Recommended coverage: - `propList` / selector blacklist interaction - dynamic `input`-based options - raw match context branches +- signed/scientific values, zero, and absolute `minValue` thresholds +- complete unit boundaries, strings/URLs, and deeply nested `var()` fallbacks +- repeated processing when output units can match another rule ## Composition Strategy diff --git a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md index 13ade99..a5443b9 100644 --- a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md +++ b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md @@ -145,6 +145,11 @@ const rule = { } ``` +回调接收带符号的数值,包括科学计数法;不要为负值再次添加负号。 +`minValue` 按绝对值比较。`context.input` 和 `context.filePath` 指向根入口; +需要区分导入声明的来源时,读取 `context.decl?.source?.input.file`。 +默认 tokenizer 与自定义 `unitRegex` 的边界见 [匹配边界](./API.zh-CN.md#匹配边界)。 + ## 文件组织 推荐目录结构: @@ -199,6 +204,9 @@ it('converts rem to dp', () => { - `propList` / selector blacklist 交互 - 基于 `input` 的动态参数 - 原始匹配信息分支 +- 带符号数值、科学计数法、零值与绝对值 `minValue` 阈值 +- 完整单位边界、字符串 / URL 与深层 `var()` fallback +- 输出单位还能匹配规则时的重复处理 ## 组合策略 diff --git a/packages/postcss-rule-unit-converter/README.md b/packages/postcss-rule-unit-converter/README.md index 6bf9bc7..fbd1262 100644 --- a/packages/postcss-rule-unit-converter/README.md +++ b/packages/postcss-rule-unit-converter/README.md @@ -19,9 +19,14 @@ Rule-driven PostCSS unit conversion plugin. - Each rule matches a source unit and produces a next value. - Rules are evaluated in order. -- If two rules can match the same source unit, the first matching rule wins. +- If two rules can match the same source unit, the first matching rule wins, even if its transform returns `undefined`. +- Each source token is converted once per processing run; a generated unit is not passed to later rules. Running the plugin again may convert that output. - Use `composeRules(...)` when you want to merge multiple preset groups and custom rules into one ordered rule list. +## Runtime Requirements + +Node.js 20.19.0 or newer and PostCSS 8 are required. + ## Usage ```ts @@ -243,21 +248,40 @@ const result = await postcss([ `RuleContext` extends the shared replace context and also exposes: -- `fromUnit`: source unit used for rule matching; it is normalized to lowercase when the plugin uses a custom or broad unit regex -- `rawUnit`: matched unit text before normalization, for example `PX` -- `rawValue`: matched numeric text before `Number(...)`, for example `40` +- `fromUnit`: lowercase source unit for rule matching; default tokenization also decodes CSS escapes +- `rawUnit`: original unit text before decoding and normalization, for example `PX` or a CSS-escaped spelling +- `rawValue`: original signed numeric text before `Number(...)`, for example `-2` or `1e3` - `match`: full matched fragment, for example `40PX` Advanced matching options: -- `unitRegex`: override the generated matching regex when you need custom parsing behavior such as matching inside `var(...)` +- `unitRegex`: replace default tokenization with your own matching regex, for example to opt into matching inside `var(...)` - `keepZeroUnit`: keep `0px`, `0rem`, or custom zero outputs instead of collapsing them to bare `0` -String unit matchers are trimmed and normalized to lowercase. With the default -generated regex, `'px'` matches lowercase `px`; use `/^px$/i` or a custom -`unitRegex` if you need uppercase unit spellings. The default regex skips -quoted strings, `url(...)`, and `var(...)`; a custom `unitRegex` replaces that -regex, so include those skip branches yourself if you still need them. +## Matching And Processing Boundaries + +Default matching uses CSS tokens. String unit matchers are trimmed and normalized +to lowercase, so `'px'` matches `px`, `PX`, and equivalent CSS-escaped units. +Signed numbers and scientific notation are parsed as complete values. A rule for +`rem` does not match the unit `remfoo` in `2remfoo` or the identifier `foo2rem`. +Strings and URLs remain untouched, including escaped quotes and uppercase `URL()`. +The entire `var()` function is skipped at every nesting depth; other functions +such as `calc()` and `min()` can contain convertible dimensions. + +Callbacks receive the signed value: `-2rem` passes `-2`, and `1e3rem` passes +`1000`. Global and per-rule `minValue` compare the absolute source magnitude, so +`minValue: 2` permits both `2rem` and `-2rem` while leaving `1rem` unchanged. + +An explicit `unitRegex` bypasses tokenization completely. It must capture the +numeric value in group 1 and the unit in group 2; a missing global flag is added. +You are responsible for token boundaries, signed/scientific numbers, and any +string, URL, or `var()` protection on that custom path. See [API.md](./API.md). + +`exclude`, `context.input`, and `context.filePath` refer to the root CSS entry, +including when an importer has expanded other files into it. For decisions based +on an imported declaration's own source, inspect `context.decl?.source?.input.file` +in a transform callback. With `replace: false`, fallback deduplication compares +property, value, and `!important`, preserving declarations with different priority. ## Rule Tips diff --git a/packages/postcss-rule-unit-converter/README.zh-CN.md b/packages/postcss-rule-unit-converter/README.zh-CN.md index 2860e81..e983d3c 100644 --- a/packages/postcss-rule-unit-converter/README.zh-CN.md +++ b/packages/postcss-rule-unit-converter/README.zh-CN.md @@ -19,9 +19,14 @@ - 每条规则负责匹配一种源单位并生成一个新值。 - 规则按顺序执行。 -- 如果多条规则都能命中同一个源单位,先写的那条优先。 +- 如果多条规则都能命中同一个源单位,先写的那条优先;即使它的 transform 返回 `undefined`,也不会继续尝试后面的规则。 +- 每轮只转换原始 token 一次,不把生成的单位交给后续规则连锁转换。再次运行插件时,仍可能转换上次的输出。 - 需要组合多组 preset 和自定义规则时,用 `composeRules(...)` 明确最终顺序。 +## 运行环境 + +需要 Node.js 20.19.0 或更高版本,以及 PostCSS 8。 + ## 用法 ```ts @@ -225,11 +230,32 @@ const result = await postcss([ - 如果你要严格控制命中顺序,优先直接写显式规则数组。 - 使用 `presets.viewportPresetGroup()` 时,建议显式指定 `viewportUnit: 'vw'` 或 `viewportUnit: 'vh'`。 - 在业务里沉淀可复用 preset 时,优先使用 `definePreset(...)` 和 `definePresetGroup(...)`。 -- `RuleContext.fromUnit` 是用于规则匹配的源单位;`rawUnit` 保留原始大小写;`rawValue` 是 `Number(...)` 前的数字文本;`match` 是完整匹配片段。 -- 字符串单位 matcher 会归一化为小写。默认正则下 `'px'` 匹配小写 `px`;需要匹配 `PX`/`Px` 时使用 `/^px$/i` 或自定义 `unitRegex`。 -- 默认正则会跳过字符串、`url(...)` 和 `var(...)`。自定义 `unitRegex` 会完全替换默认正则;如果仍要跳过这些内容,需要在自定义正则里保留跳过分支。 +- `RuleContext.fromUnit` 是归一化为小写的源单位(默认 tokenizer 还会解码转义);`rawUnit` 保留原始大小写与转义;`rawValue` 是 `Number(...)` 前带符号的数字文本;`match` 是完整匹配片段。 +- 如果需要主动处理 `var()` 中的内容,可以使用自定义 `unitRegex`,并自行保证匹配边界。 - 如果你想直接抄场景配置,优先看 cookbook。 +## 匹配与处理边界 + +默认使用 CSS tokenizer 匹配。字符串单位 matcher 会先 trim 并归一化为小写, +因此 `'px'` 能匹配 `px`、`PX` 及等价的 CSS 转义写法。带符号数字和科学计数法 +按完整数值解析。`rem` 规则不会误改 `2remfoo` 的 `remfoo` 单位,也不会误改 +`foo2rem` 这样的标识符。字符串和 URL 保持原样,包括转义引号和大写 `URL()`。 +默认完整跳过任意嵌套深度的 `var()`;`calc()`、`min()` 等其他函数里的长度仍可转换。 + +回调收到真实带符号数值:`-2rem` 传入 `-2`,`1e3rem` 传入 `1000`。 +全局与单规则的 `minValue` 都按源值绝对值比较,例如 `minValue: 2` 会转换 +`2rem` 和 `-2rem`,保留 `1rem`。 + +显式 `unitRegex` 会完全绕过 tokenizer。数字必须放在第 1 个捕获组,单位放在 +第 2 个捕获组;缺少的 `g` 标志会自动补上。这个路径上的 token 边界、带符号数字、 +科学计数法,以及字符串、URL、`var()` 保护均由自定义正则负责。详见 +[API.zh-CN.md](./API.zh-CN.md)。 + +`exclude`、`context.input` 和 `context.filePath` 指向根 CSS 入口,即使 importer +已将其他文件展开进来。需要按导入声明的原文件处理时,在 transform 中读取 +`context.decl?.source?.input.file`。`replace: false` 的 fallback 去重同时比较 +属性、值和 `!important`,不会把优先级不同的声明当作重复项。 + ## Presets 自定义 preset 辅助类型: diff --git a/packages/postcss-rule-unit-converter/package.json b/packages/postcss-rule-unit-converter/package.json index 43f043f..bc3efe2 100644 --- a/packages/postcss-rule-unit-converter/package.json +++ b/packages/postcss-rule-unit-converter/package.json @@ -50,6 +50,9 @@ "files": [ "dist" ], + "engines": { + "node": ">=20.19.0" + }, "scripts": { "dev": "tsdown -w", "build": "tsdown", @@ -66,6 +69,7 @@ "postcss": "^8" }, "dependencies": { + "@csstools/css-tokenizer": "4.0.2", "postcss-plugin-shared": "workspace:*" }, "tsd": { diff --git a/packages/postcss-rule-unit-converter/src/plugin.ts b/packages/postcss-rule-unit-converter/src/plugin.ts index eaf9fd0..cae1ae9 100644 --- a/packages/postcss-rule-unit-converter/src/plugin.ts +++ b/packages/postcss-rule-unit-converter/src/plugin.ts @@ -5,14 +5,14 @@ import type { UnitMatcher, UserDefinedOptions, } from './types' +import { Input } from 'postcss' import { - createAnyUnitRegex, - createUnitRegex, getConfig, postcssPlugin, toFixed, walkAndReplaceValues, } from './shared' +import { createValueTransformer } from './tokenize' interface StringMatcherEntry { matcher: string @@ -126,7 +126,6 @@ function normalizeRules(rules: readonly ConversionRule[]) { } } - const hasComplexMatcher = entries.some(entry => entry.type !== 'string') const stringRules = new Map() for (const entry of entries) { if (entry.type === 'string' && !stringRules.has(entry.unit)) { @@ -137,7 +136,6 @@ function normalizeRules(rules: readonly ConversionRule[]) { return { entries, stringRules, - hasComplexMatcher, } } @@ -219,30 +217,29 @@ function createReplace( unitPrecision: number, minValue: number, keepZeroUnit: boolean, - normalizeUnitCase: boolean, context: Omit, ) { const shouldRound = unitPrecision >= 0 && unitPrecision <= 100 - return function replace(m: string, $1?: string, $2?: string) { + return function replace(m: string, $1?: string, $2?: string, decodedUnit = $2) { if (!$1 || !$2) { return m } const value = Number($1) - if (Number.isNaN(value)) { + if (!Number.isFinite(value)) { return m } const rawUnit = $2 - const fromUnit = normalizeUnitCase ? rawUnit.toLowerCase() : rawUnit + const fromUnit = decodedUnit!.toLowerCase() const rule = getRule(fromUnit) if (!rule) { return m } const effectiveMinValue = rule.minValue ?? minValue - if (value < effectiveMinValue) { + if (Math.abs(value) < effectiveMinValue) { return m } @@ -273,7 +270,8 @@ function createReplace( return '0' } - return `${roundedValue}${normalized.unit}` + const sign = $1.startsWith('+') && roundedValue >= 0 ? '+' : '' + return `${sign}${roundedValue}${normalized.unit}` } } @@ -297,39 +295,25 @@ const plugin: PostcssUnitConverter = (options: UserDefinedOptions = {}) => { return { postcssPlugin } } - const { entries, stringRules, hasComplexMatcher } = normalizeRules(rules) + const { entries, stringRules } = normalizeRules(rules) if (entries.length === 0) { return { postcssPlugin } } - let unitRegex: RegExp - let getRule: (unit: string) => ConversionRule | undefined - let normalizeUnitCase = true - if (customUnitRegex) { if (countCapturingGroups(customUnitRegex.source) < 2) { throw new TypeError('unitRegex must provide numeric and unit capture groups') } - unitRegex = customUnitRegex - getRule = unit => getMatcherRuleWithStrings(entries, unit, stringRules) - } - else if (hasComplexMatcher) { - unitRegex = createAnyUnitRegex() - getRule = unit => getMatcherRuleWithStrings(entries, unit, stringRules) - } - else { - const units = Array.from(stringRules?.keys() ?? []).sort((a, b) => b.length - a.length) - if (units.length === 0) { - return { postcssPlugin } - } - unitRegex = createUnitRegex(units) - getRule = unit => stringRules?.get(unit)?.rule - normalizeUnitCase = false } return { postcssPlugin, - Once(css) { + Once(css, helpers) { + // Older wrappers call this hook directly. Without either a source Input + // or native PostCSS helpers, retain their historical no-op behavior. + if (!css.source?.input && !helpers) { + return + } type SharedWalkAndReplaceOptions = Parameters[0] // A declaration often contains the same unit several times (for example // `margin: 1rem 2rem 1rem 2rem`). Complex matchers have to scan the @@ -341,30 +325,41 @@ const plugin: PostcssUnitConverter = (options: UserDefinedOptions = {}) => { if (resolvedRules.has(unit)) { return resolvedRules.get(unit) } - const rule = getRule(unit) + const rule = getMatcherRuleWithStrings(entries, unit, stringRules) resolvedRules.set(unit, rule) return rule } const walkOptions: SharedWalkAndReplaceOptions = { root: css as unknown as SharedWalkAndReplaceOptions['root'], - unitRegex, + // Programmatic ASTs may have no source. Supply callback/filter context + // without attaching invented mapping metadata to any AST node. + input: css.source?.input ?? new Input(css.toString(), { from: helpers.result.opts.from, map: false }), propList, selectorBlackList, exclude, replace, mediaQuery, - createReplacer: (context) => { - return createReplace( - resolveRule, - unitPrecision, - minValue, - keepZeroUnit, - normalizeUnitCase, - context, - ) - }, shouldProcessAtRule: atRule => atRule.name === 'media', } + const createConverter = (context: Parameters[4]) => createReplace( + resolveRule, + unitPrecision, + minValue, + keepZeroUnit, + context, + ) + if (customUnitRegex) { + walkOptions.unitRegex = customUnitRegex + walkOptions.createReplacer = (context) => { + const convert = createConverter(context) + // String#replace passes an offset after the captured unit; do not + // confuse that argument with the decoded CSS unit used by tokens. + return (match: string, rawValue?: string, rawUnit?: string) => convert(match, rawValue, rawUnit) + } + } + else { + walkOptions.createValueTransformer = context => createValueTransformer(createConverter(context)) + } walkAndReplaceValues(walkOptions) }, } diff --git a/packages/postcss-rule-unit-converter/src/tokenize.ts b/packages/postcss-rule-unit-converter/src/tokenize.ts new file mode 100644 index 0000000..82d9420 --- /dev/null +++ b/packages/postcss-rule-unit-converter/src/tokenize.ts @@ -0,0 +1,60 @@ +import { tokenizer, TokenType } from '@csstools/css-tokenizer' + +// A dimension/percentage token always starts with a CSS number. Tokenization +// determines its boundary; this pattern only recovers the original spelling +// for callback context, including the sign and exponent. +const NUMBER_PREFIX = /^[+-]?(?:\d*\.\d+|\d+)(?:e[+-]?\d+)?/i + +type ConvertDimension = (match: string, rawValue: string, rawUnit: string, decodedUnit: string) => string + +export function createValueTransformer(convert: ConvertDimension) { + return (value: string) => { + const stream = tokenizer({ css: value }) + const skippedBlocks: TokenType[] = [] + let output = '' + + while (true) { + const token = stream.nextToken() + const type = token[0] + if (type === TokenType.EOF) { + return output + } + + if (skippedBlocks.length > 0) { + if (type === TokenType.Function || type === TokenType.OpenParen) { + skippedBlocks.push(TokenType.CloseParen) + } + else if (type === TokenType.OpenSquare) { + skippedBlocks.push(TokenType.CloseSquare) + } + else if (type === TokenType.OpenCurly) { + skippedBlocks.push(TokenType.CloseCurly) + } + else if (type === skippedBlocks.at(-1)) { + skippedBlocks.pop() + } + output += token[1] + continue + } + + if (type === TokenType.Function) { + const name = token[4].value.toLowerCase() + // Unquoted URLs are atomic URL/BadURL tokens; quoted URLs are function + // tokens. Escaped and mixed-case spellings have already been decoded. + if (name === 'var' || name === 'url') { + skippedBlocks.push(TokenType.CloseParen) + } + } + + if (type === TokenType.Dimension || type === TokenType.Percentage) { + const rawValue = token[1].match(NUMBER_PREFIX)![0] + const rawUnit = token[1].slice(rawValue.length) + const decodedUnit = type === TokenType.Dimension ? token[4].unit : '%' + output += convert(token[1], rawValue, rawUnit, decodedUnit) + } + else { + output += token[1] + } + } + } +} diff --git a/packages/postcss-rule-unit-converter/src/types.ts b/packages/postcss-rule-unit-converter/src/types.ts index cde85ca..0d4491a 100644 --- a/packages/postcss-rule-unit-converter/src/types.ts +++ b/packages/postcss-rule-unit-converter/src/types.ts @@ -10,8 +10,11 @@ export type UnitMatcher = string | RegExp | ((unit: string) => boolean) * Extra context passed to conversion callbacks. */ export interface RuleContext extends ReplaceContext { + /** CSS-decoded unit, normalized to lowercase. */ fromUnit: string + /** Original unit spelling, including CSS escapes and letter case. */ rawUnit: string + /** Original number spelling, including its sign and exponent. */ rawValue: string match: string } @@ -28,6 +31,7 @@ export interface ConvertedValue { * Per-unit conversion callback used by `presets.unitsToPx`. */ export type UnitTransform = ( + /** Signed numeric value from the CSS token. */ value: number, context: RuleContext, ) => number | ConvertedValue | undefined @@ -63,12 +67,15 @@ export type RuleTransform = UnitTransform * Single conversion rule. * * Rules are matched in order. If multiple rules can match the same source - * unit, the first one wins. + * unit, the first one wins, even if it declines conversion. Results are not + * matched against subsequent rules in the same pass; another plugin run can + * convert them again. */ export interface ConversionRule { from: UnitMatcher to?: string factor?: number + /** Minimum absolute magnitude of the original value. */ minValue?: number transform?: RuleTransform } @@ -92,8 +99,13 @@ export type NumericResolver = number | ((input: Input) => number) */ export interface UserDefinedOptions { rules?: readonly ConversionRule[] + /** + * Override CSS tokenization with custom numeric and unit capture groups. + * The expression controls its own token boundaries and protected functions. + */ unitRegex?: RegExp | undefined unitPrecision?: number + /** Minimum absolute magnitude of the original value. Defaults to 0. */ minValue?: number keepZeroUnit?: boolean selectorBlackList?: readonly (string | RegExp)[] @@ -104,6 +116,10 @@ export interface UserDefinedOptions { propList?: readonly (string | RegExp)[] replace?: boolean mediaQuery?: boolean + /** + * Excludes the stylesheet using its root input path. Imported declarations + * retain their own source in context.decl.source for per-source callbacks. + */ exclude?: readonly (string | RegExp)[] | ((filePath: string) => boolean) disabled?: boolean } diff --git a/packages/postcss-rule-unit-converter/test-d/index.test-d.ts b/packages/postcss-rule-unit-converter/test-d/index.test-d.ts index d973a38..ee02b52 100644 --- a/packages/postcss-rule-unit-converter/test-d/index.test-d.ts +++ b/packages/postcss-rule-unit-converter/test-d/index.test-d.ts @@ -4,6 +4,8 @@ import plugin, { composeRules, presets, type ConversionRule, + type RuleContext, + type UnitTransform, type UserDefinedOptions, } from '../dist/index.mjs' import { remToPx } from '../dist/presets.mjs' @@ -14,3 +16,19 @@ expectType(remToPx({ rootValue: 16 })) expectType(composeRules(presets.remToPx(), presets.pxToRem())) plugin({ rules: [presets.remToPx()] }) expectError(plugin({ rules: [{ from: 'rem', factor: '16' }] })) + +const signedTransform: UnitTransform = (value, context) => { + expectType(value) + expectType(context) + expectType(context.fromUnit) + expectType(context.rawUnit) + expectType(context.rawValue) + expectType(context.filePath) + return { value: -value, unit: 'rpx' } +} +plugin({ + rules: [{ from: unit => unit === 'rem', transform: signedTransform, minValue: 1 }], + unitRegex: /([+-]?\d+)(rem)/g, + mediaQuery: true, + exclude: path => path.includes('vendor'), +}) diff --git a/packages/postcss-rule-unit-converter/test/index.test.ts b/packages/postcss-rule-unit-converter/test/index.test.ts index f438ecf..bc290fd 100644 --- a/packages/postcss-rule-unit-converter/test/index.test.ts +++ b/packages/postcss-rule-unit-converter/test/index.test.ts @@ -3,6 +3,7 @@ import type { ConversionRule, UnitMatcher, UnitRule } from '../src/index' import postcss from 'postcss' import unitConverter, { composeRules, + createAnyUnitRegex, createUnitRegex, definePreset, definePresetGroup, @@ -32,6 +33,14 @@ describe('postcss-rule-unit-converter', () => { expect(output).toBe('a:1PX;b:2P;') }) + it('retains explicitly exported regex helpers for custom matching', () => { + const regex = createAnyUnitRegex() + const matches = [...'1foo 2u2 3% "4rem" var(--x, 5rem) url(6rem.png)'.matchAll(regex)] + .filter(match => match[1]) + .map(match => [match[1], match[2]]) + expect(matches).toEqual([['1', 'foo'], ['2', 'u2'], ['3', '%']]) + }) + it('converts with custom rules', () => { const input = '.rule { font-size: 1rem; width: 100px; }' const output = '.rule { font-size: 16px; width: 26.66667vw; }' @@ -82,6 +91,23 @@ describe('postcss-rule-unit-converter', () => { expect(processed).toBe(output) }) + it('keeps deliberately unmapped units and ignores empty map entries', () => { + const rules = presets.unitsToPx({ + unitMap: [undefined, [' ', 999], ['rem', null], ['foo', undefined], ['bar', 2]] as any, + }) + const processed = postcss(unitConverter({ rules })).process('.a { a: 1rem; b: 2foo; c: 3bar; }').css + + expect(processed).toBe('.a { a: 1rem; b: 2foo; c: 6px; }') + expect(rules).toHaveLength(3) + }) + + it('supports the direct rem-to-rpx alias and explicit output overrides', () => { + expect(postcss(unitConverter({ rules: [presets.remToRpx()] })).process('.a { width: 2rem; }').css) + .toBe('.a { width: 32rpx; }') + expect(postcss(unitConverter({ rules: [presets.remToRpx({ to: 'px', rootValue: 20 })] })).process('.a { width: 2rem; }').css) + .toBe('.a { width: 40px; }') + }) + it('preserves Map and Array unit map order without merging defaults', () => { const input = '.rule { width: 1rem; height: 2foo; margin: 3bar; padding: 4baz; }' const output = '.rule { width: 1rem; height: 4px; margin: 9px; padding: 4baz; }' @@ -124,6 +150,8 @@ describe('postcss-rule-unit-converter', () => { })).process(input, { from: '/src/design.css' }).css expect(processed).toBe(output) + expect(postcss(unitConverter({ rules: [presets.remToPx({ rootValue: () => 20 })] })) + .process('h1 { font-size: 1rem; }').css).toBe('h1 { font-size: 20px; }') }) it('respects replace=false, propList, mediaQuery, and selector blacklist', () => { diff --git a/packages/postcss-rule-unit-converter/test/tokenize.test.ts b/packages/postcss-rule-unit-converter/test/tokenize.test.ts new file mode 100644 index 0000000..c0afc05 --- /dev/null +++ b/packages/postcss-rule-unit-converter/test/tokenize.test.ts @@ -0,0 +1,246 @@ +import type { Plugin } from 'postcss' +import type { RuleContext, UserDefinedOptions } from '../src/index' +import path from 'node:path' +import postcss from 'postcss' +import unitConverter from '../src/index' +import { createValueTransformer } from '../src/tokenize' + +function convert(input: string, options: UserDefinedOptions = {}) { + return postcss(unitConverter({ + rules: [{ from: 'rem', to: 'px', factor: 16 }], + ...options, + })).process(input, { from: '/src/entry.css' }).css +} + +describe('CSS token conversion', () => { + it('converts complete signed, fractional, and scientific numeric tokens', () => { + expect(convert('.a { margin: -2rem +2rem .5rem -.5rem +.5rem 1e3rem 1e-3rem -1E+2rem; }')) + .toBe('.a { margin: -32px +32px 8px -8px +8px 16000px 0.016px -1600px; }') + }) + + it('provides the signed number and original spellings with decoded unit matching', () => { + const contexts: Array<{ value: number, from: string, raw: string, number: string, match: string }> = [] + const css = convert(String.raw`.a { a: -2REM; b: +.5r\65 m; c: 1e-3\72 em; }`, { + rules: [{ + from: ' Rem ', + to: 'px', + transform(value, context) { + contexts.push({ value, from: context.fromUnit, raw: context.rawUnit, number: context.rawValue, match: context.match }) + return value + 1 + }, + }], + }) + + expect(css).toBe('.a { a: -1px; b: +1.5px; c: 1.001px; }') + expect(contexts).toEqual([ + { value: -2, from: 'rem', raw: 'REM', number: '-2', match: '-2REM' }, + { value: 0.5, from: 'rem', raw: String.raw`r\65 m`, number: '+.5', match: String.raw`+.5r\65 m` }, + { value: 0.001, from: 'rem', raw: String.raw`\72 em`, number: '1e-3', match: String.raw`1e-3\72 em` }, + ]) + }) + + it('does not convert partial identifiers, complete longer units, strings, comments, or URLs', () => { + const value = String.raw`2remfoo foo2rem _2rem --2rem 2rem-foo 像素2rem 2像素 "escaped\" 2rem" 'escaped\' 2rem' url(2rem.png) URL(2rem.png) u\72l(2rem.png) URL("2rem.png") /* 2rem */ 2rem` + const transform = createValueTransformer((match, number, _rawUnit, unit) => unit.toLowerCase() === 'rem' ? `${Number(number) * 16}px` : match) + + expect(transform(value)).toBe(`${value.slice(0, -4)}32px`) + expect(transform('url(a"2rem) 3rem')).toBe('url(a"2rem) 48px') + expect(transform('url(2rem')).toBe('url(2rem') + expect(transform('URL("2rem" 3rem) 4rem')).toBe('URL("2rem" 3rem) 64px') + }) + + it('skips var() entirely at any depth and accepts escaped and uppercase function names', () => { + const input = String.raw`.a { a: calc(1rem + var(--x, min(2rem, max(3rem, calc(4rem + 5rem)))) + 6rem); b: VAR(--x, 7rem); c: v\61 r(--x, 8rem); }` + const output = String.raw`.a { a: calc(16px + var(--x, min(2rem, max(3rem, calc(4rem + 5rem)))) + 96px); b: VAR(--x, 7rem); c: v\61 r(--x, 8rem); }` + + expect(convert(input)).toBe(output) + const transform = createValueTransformer((_match, number) => `${Number(number) * 16}px`) + expect(transform('var(--x, [1rem {2rem} (3rem)]) 4rem')).toBe('var(--x, [1rem {2rem} (3rem)]) 64px') + expect(transform('var(--x, [) 1rem] ) 2rem')).toBe('var(--x, [) 1rem] ) 32px') + expect(transform('var(--x, calc(1rem')).toBe('var(--x, calc(1rem') + }) + + it('supports percentages, non-ASCII units, and case-normalized complex matchers', () => { + expect(convert(String.raw`.a { a: 25%; b: 3像素; c: 2FOO; d: 2\66 oo; }`, { + rules: [ + { from: '%', to: 'px', factor: 2 }, + { from: '像素', to: 'px', factor: 3 }, + { from: unit => unit === 'foo', to: 'px', factor: 4 }, + ], + })).toBe('.a { a: 50px; b: 9px; c: 8px; d: 8px; }') + }) + + it('uses absolute magnitude for global and per-rule minima while keeping zero behavior', () => { + const input = '.a { a: -2rem; b: -0.5rem; c: 2rem; d: 0rem; e: -0rem; f: 0.5rem; }' + expect(convert(input, { minValue: 1 })).toBe('.a { a: -32px; b: -0.5rem; c: 32px; d: 0rem; e: -0rem; f: 0.5rem; }') + expect(convert(input, { minValue: 10, rules: [{ from: 'rem', to: 'px', factor: 16, minValue: 0.5 }] })) + .toBe('.a { a: -32px; b: -8px; c: 32px; d: 0rem; e: -0rem; f: 8px; }') + expect(convert('.a { a: -0rem; b: +0rem; c: 0rem; }')).toBe('.a { a: 0; b: 0; c: 0; }') + expect(convert('.a { a: -0rem; b: +0rem; c: 0rem; }', { keepZeroUnit: true })).toBe('.a { a: 0px; b: +0px; c: 0px; }') + }) + + it('negates signed values without duplicate signs and rounds at configured precision', () => { + expect(convert('.a { a: -2rem; b: +2rem; c: 2rem; }', { + rules: [{ from: 'rem', to: 'px', transform: value => -value }], + })).toBe('.a { a: 2px; b: -2px; c: -2px; }') + expect(convert('.a { a: -1.005rem; b: 0.00001rem; }', { + rules: [{ from: 'rem', to: 'px', factor: 1 }], + unitPrecision: 2, + })).toBe('.a { a: -1.01px; b: 0; }') + }) + + it('does not match converted output against subsequent rules, but permits another pass', () => { + const options = { rules: [{ from: 'rem', to: 'px', factor: 2 }, { from: 'px', to: 'rem', factor: 3 }] } + const first = convert('.a { width: 1rem; }', options) + expect(first).toBe('.a { width: 2px; }') + expect(convert(first, options)).toBe('.a { width: 6rem; }') + expect(convert('.a { width: 1rem; }', { + rules: [{ from: /^rem$/, transform: () => undefined }, { from: 'rem', to: 'px', factor: 2 }], + })).toBe('.a { width: 1rem; }') + }) + + it('separates important fallback declarations and remains finite when a processor is reused', () => { + const input = '.a { width: 1rem !important; width: 16px; height: 1rem; height: 16px !important; }' + expect(convert(input, { replace: false })) + .toBe('.a { width: 1rem !important; width: 16px !important; width: 16px; height: 1rem; height: 16px; height: 16px !important; }') + const processor = postcss(unitConverter({ rules: [{ from: 'rem', to: 'px', factor: 16 }], replace: false })) + const first = processor.process('.a { width: 1rem !important; width: 16px !important; }', { from: '/src/first.css' }).css + expect(first).toBe('.a { width: 1rem !important; width: 16px !important; }') + expect(processor.process(first, { from: '/src/again.css' }).css).toBe(first) + }) + + it('resolves dynamic unit matchers separately for each root when a processor is reused', () => { + let enabled = false + const processor = postcss(unitConverter({ + rules: [{ from: unit => enabled && unit === 'rem', to: 'px', factor: 16 }], + })) + expect(processor.process('.a { width: 1rem; }', { from: '/src/first.css' }).css).toBe('.a { width: 1rem; }') + enabled = true + expect(processor.process('.a { width: 2rem; }', { from: '/src/second.css' }).css).toBe('.a { width: 32px; }') + }) + + it('keeps custom regex capture and protection semantics independent of tokenization', () => { + expect(convert('.a { width: var(--x, -2rem); label: item2rem; }', { + rules: [{ from: 'rem', to: 'px', transform: value => value + 1 }], + unitRegex: /([+-]?\d+)(rem)/g, + })).toBe('.a { width: var(--x, -1px); label: item3px; }') + }) + + it('uses tokenized media params with the same filtering and whole-value protection', () => { + const input = '@media (min-width: 1e2REM) { .a { width: 1rem; margin: 2rem; } .skip { width: 3rem; } } @supports (width: 1rem) { .b { width: 4rem; } }' + expect(convert(input, { propList: ['width'], selectorBlackList: ['.skip'], mediaQuery: true })) + .toBe('@media (min-width: 1600px) { .a { width: 16px; margin: 2rem; } .skip { width: 3rem; } } @supports (width: 1rem) { .b { width: 64px; } }') + }) + + it('retains sources and source maps while exposing the entry context', () => { + const root = postcss.parse('.a { width: 2rem; }', { from: '/vendor/imported.css' }) + const entry = postcss.parse('', { from: '/src/entry.css' }) + entry.append(root.nodes) + let seen: RuleContext | undefined + const result = postcss(unitConverter({ + rules: [{ + from: 'rem', + to: 'px', + transform(value, context) { + seen = context + return value * 16 + }, + }], + exclude: ['imported.css'], + })).process(entry, { from: '/src/entry.css', to: '/dist/output.css', map: { inline: false } }) + + expect(result.css).toContain('32px') + expect(seen?.filePath).toBe(entry.source!.input.file) + expect(seen?.decl?.source?.input.file).toBe(root.source!.input.file) + expect(result.map?.toJSON().sources).toContain('../vendor/imported.css') + }) + + it('does not convert non-finite numbers or invalid callback results', () => { + const input = '.a { a: 1e999rem; b: 2foo; c: 2bar; d: 2baz; e: 2qux; }' + expect(convert(input, { + rules: [ + { from: 'rem', to: 'px', factor: 2 }, + { from: 'foo', transform: () => 'invalid' as any }, + { from: 'bar', transform: () => ({}) as any }, + { from: 'baz', transform: () => ({ value: '2' }) as any }, + { from: 'qux', transform: () => ({ value: 2, unit: 42 }) as any }, + ], + })).toBe(input) + }) + + it('validates named captures and ignores non-capturing groups, lookarounds, escaped and bracketed parentheses', () => { + expect(convert('.a { width: 2rem; }', { unitRegex: /(?\d+)(?rem)/g })) + .toBe('.a { width: 32px; }') + expect(convert('.a { width: (2rem); }', { unitRegex: /(?<=\()(\d+)(rem)(?=[()])/g })) + .toBe('.a { width: (32px); }') + expect(convert('.a { width: x2rem 2rem; }', { unitRegex: /(? { + expect(convert('.a { width: 2rem; }', { rules: [{ from: 'rem' }, { from: 'rem', to: 'px', factor: 16 }] })) + .toBe('.a { width: 2rem; }') + expect(convert('.a { width: 2foo; }', { rules: [{ from: /^bar$/, to: 'px', factor: 16 }] })) + .toBe('.a { width: 2foo; }') + }) + + it.each([undefined, path.resolve('src/explicit.css')])('converts source-less ASTs with ephemeral input context: %s', (from) => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: '2rem' })) + const contexts: RuleContext[] = [] + const result = postcss(unitConverter({ + rules: [{ + from: 'rem', + to: 'px', + transform(value, context) { + contexts.push(context) + return value * 16 + }, + }], + })).process(root, { from, map: { inline: false, annotation: false } }) + + expect(result.css).toContain('width: 32px') + expect(contexts[0]!.filePath).toBe(from) + expect(contexts[0]!.input.file).toBe(from) + expect(contexts[0]!.input.css).toContain('width: 2rem') + expect(root.source).toBeUndefined() + root.walk(node => expect(node.source).toBeUndefined()) + expect(result.map!.toJSON().sources).toEqual(['']) + }) + + it('applies explicit entry exclusions to source-less ASTs', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: '2rem' })) + const transform = vi.fn(value => value * 16) + const processor = postcss(unitConverter({ rules: [{ from: 'rem', to: 'px', transform }], exclude: ['excluded.css'] })) + + expect(processor.process(root, { from: '/src/excluded.css' }).sync().root.toString()).toContain('width: 2rem') + expect(transform).not.toHaveBeenCalled() + expect(root.source).toBeUndefined() + }) + + it('retains direct legacy hook behavior with and without source Input', () => { + const once = (unitConverter({ rules: [{ from: 'rem', to: 'px', factor: 16 }] }) as Plugin).Once! + const callLegacyHook = once as (root: postcss.Root) => void + const sourceLess = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: '2rem' })) + expect(() => callLegacyHook(sourceLess)).not.toThrow() + expect(sourceLess.toString()).toContain('width: 2rem') + const sourced = postcss.parse('.a{width:2rem}') + callLegacyHook(sourced) + expect(sourced.toString()).toBe('.a{width:32px}') + }) + + it('does not parse source-map annotations when creating ephemeral callback context', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'width', value: '2rem' })).append(postcss.comment({ text: '# sourceMappingURL=data:application/json;base64,invalid' })) + const result = postcss(unitConverter({ rules: [{ from: 'rem', to: 'px', factor: 16 }] })) + .process(root, { from: undefined }) + .sync() + + expect(result.root.toString()).toContain('width: 32px') + expect(result.root.toString()).toContain('sourceMappingURL=data:application/json;base64,invalid') + expect(root.source).toBeUndefined() + }) +}) diff --git a/packages/postcss-units-to-px/src/plugin.ts b/packages/postcss-units-to-px/src/plugin.ts index 265955c..db817e5 100644 --- a/packages/postcss-units-to-px/src/plugin.ts +++ b/packages/postcss-units-to-px/src/plugin.ts @@ -1,3 +1,4 @@ +import type { Plugin } from 'postcss' import type { ConversionRule, RuleContext, @@ -161,13 +162,11 @@ const plugin: PostcssUnitsToPx = (options: UserDefinedOptions = {}) => { rules, selectorBlackList, unitPrecision, - }) as { Once?: (css: unknown) => void } + }) as Plugin return { postcssPlugin, - Once(css) { - wrappedPlugin.Once?.(css) - }, + Once: wrappedPlugin.Once!, } } diff --git a/packages/postcss-units-to-px/test/lifecycle.test.ts b/packages/postcss-units-to-px/test/lifecycle.test.ts new file mode 100644 index 0000000..17f5a90 --- /dev/null +++ b/packages/postcss-units-to-px/test/lifecycle.test.ts @@ -0,0 +1,12 @@ +import postcss from 'postcss' +import unitsToPx from '../src/index' + +it('preserves native PostCSS context for source-less AST input', () => { + const root = postcss.root().append(postcss.rule({ selector: '.a' }).append({ prop: 'font-size', value: '2rem' })) + const result = postcss(unitsToPx({ unitMap: { rem: (value, context) => context.input.file?.endsWith('explicit.css') ? value * 20 : value * 16 } })) + .process(root, { from: 'explicit.css' }) + .sync() + + expect(result.root.toString()).toContain('font-size: 40px') + expect(root.source).toBeUndefined() +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 98c2839..3a9f1cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,10 +198,10 @@ importers: version: 4.1.1(conventional-changelog-conventionalcommits@10.4.0) '@icebreakers/eslint-config': specifier: 8.0.2 - version: 8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1) + version: 8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(globals@17.12.0)(supports-color@10.2.2))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1) '@icebreakers/stylelint-config': specifier: 5.1.2 - version: 5.1.2(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3) + version: 5.1.2(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3) '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -252,7 +252,7 @@ importers: version: 1.52.0 eslint: specifier: ^10.11.0 - version: 10.11.0(jiti@2.7.0) + version: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) execa: specifier: ^10.0.1 version: 10.0.1 @@ -303,7 +303,7 @@ importers: version: 6.1.0(postcss@8.5.28) repoctl: specifier: 5.5.5 - version: 5.5.5(@commitlint/cli@21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@types/node@26.6.2)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vitest/coverage-v8@5.0.1)(conventional-changelog-conventionalcommits@10.4.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(magicast@0.5.5)(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typanion@3.14.0)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1))(vitest@5.0.1) + version: 5.5.5(6902e60b5cf843af70b90e42cb6d7e42) rimraf: specifier: ^6.1.3 version: 6.1.3 @@ -312,7 +312,7 @@ importers: version: 4.1.0 stylelint: specifier: ^17.15.0 - version: 17.15.0(typescript@6.0.3) + version: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) tailwindcss: specifier: ^4.3.3 version: 4.3.3 @@ -339,7 +339,7 @@ importers: version: 6.0.3 vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + version: 6.1.1(supports-color@10.2.2)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) vitest: specifier: ~5.0.1 version: 5.0.1(@types/node@26.6.2)(@vitest/coverage-v8@5.0.1)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) @@ -406,6 +406,9 @@ importers: packages/postcss-rule-unit-converter: dependencies: + '@csstools/css-tokenizer': + specifier: 4.0.2 + version: 4.0.2 postcss: specifier: 8.5.28 version: 8.5.28 @@ -832,6 +835,10 @@ packages: resolution: {integrity: sha512-bPlN9S9O1A0euCpEWE4qnvB5YDuyYVsUTrxSgmAM1Is0j4tICHoVyOVAXfWMP/kS9ZrjvyIXWV2PmomiAXXqOw==} engines: {node: '>=20.19.0'} + '@csstools/css-tokenizer@4.0.2': + resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==} + engines: {node: '>=20.19.0'} + '@csstools/media-query-list-parser@5.0.0': resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} engines: {node: '>=20.19.0'} @@ -6781,12 +6788,12 @@ packages: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} - stylelint-config-html@1.1.0: - resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} - engines: {node: ^12 || >=14} + stylelint-config-html@2.0.0: + resolution: {integrity: sha512-Lk1NPEdUxzHkPv3ehktjpiOk4MPaqQ3H8fkvhxdWlQpaZCm9ze0SoMbRQLqkUxEMfPE1G9/x968uxm/+d2njoA==} + engines: {node: ^22.12 || >=24} peerDependencies: - postcss-html: ^1.0.0 - stylelint: '>=14.0.0' + postcss-html: ^2.0.0 + stylelint: '>=16.0.0' stylelint-config-recess-order@7.8.0: resolution: {integrity: sha512-ObYusdkXavm8ulgb5xvmU1l8wgCdveNYG12hOTUEdjS8bNhSRggl4or0mE0JaOnQz+Ve2xZemVpwqERCgJbntg==} @@ -7778,53 +7785,53 @@ snapshots: dependencies: '@algolia/client-common': 5.59.0 - '@antfu/eslint-config@9.5.1(@eslint-react/eslint-plugin@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.43)(eslint-plugin-format@2.0.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-react-refresh@0.5.7(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1)': + '@antfu/eslint-config@9.5.1(058b4d66dc2f2f993468ce5fdf26363b)': dependencies: '@antfu/install-pkg': 2.1.0 '@clack/prompts': 1.8.1 - '@e18e/eslint-plugin': 0.8.1(eslint@10.11.0(jiti@2.7.0)) - '@eslint-community/eslint-plugin-eslint-comments': 4.8.1(eslint@10.11.0(jiti@2.7.0)) - '@eslint/markdown': 8.0.3 - '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@vitest/eslint-plugin': 1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)(vitest@5.0.1) + '@e18e/eslint-plugin': 0.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + '@eslint-community/eslint-plugin-eslint-comments': 4.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + '@eslint/markdown': 8.0.3(supports-color@10.2.2) + '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@vitest/eslint-plugin': 1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@5.0.1) ansis: 4.4.0 cac: 7.0.0 - eslint: 10.11.0(jiti@2.7.0) - eslint-config-flat-gitignore: 2.4.0(eslint@10.11.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-config-flat-gitignore: 2.4.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) eslint-flat-config-utils: 3.2.0 - eslint-merge-processors: 2.0.0(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-antfu: 3.2.3(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-command: 4.0.0(@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-import-lite: 0.6.0(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-jsdoc: 64.5.4(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-jsonc: 3.4.2(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-n: 18.3.0(eslint@10.11.0(jiti@2.7.0))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3) + eslint-merge-processors: 2.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-antfu: 3.2.3(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-command: 4.0.0(@typescript-eslint/typescript-estree@8.70.1(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-import-lite: 0.6.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-jsdoc: 64.5.4(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-jsonc: 3.4.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-n: 18.3.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3) eslint-plugin-no-only-tests: 3.4.0 - eslint-plugin-perfectionist: 5.12.0(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-pnpm: 1.9.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-regexp: 3.3.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-toml: 1.5.0(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-unicorn: 74.0.0(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-vue: 10.11.0(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0))) - eslint-plugin-yml: 3.8.1(eslint@10.11.0(jiti@2.7.0)) - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.43)(eslint@10.11.0(jiti@2.7.0)) + eslint-plugin-perfectionist: 5.12.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-pnpm: 1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-regexp: 3.3.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-toml: 1.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-unicorn: 74.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-vue: 10.11.0(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)) + eslint-plugin-yml: 3.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.43)(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) globals: 17.12.0 local-pkg: 1.2.1 parse-gitignore: 2.0.0 toml-eslint-parser: 1.0.3 - vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)) + vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) yaml-eslint-parser: 2.1.0 optionalDependencies: - '@eslint-react/eslint-plugin': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/eslint-plugin': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@next/eslint-plugin-next': 16.2.4 - '@unocss/eslint-plugin': 66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-format: 2.0.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-react-refresh: 0.5.7(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-vuejs-accessibility: 2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0) + '@unocss/eslint-plugin': 66.6.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-format: 2.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-refresh: 0.5.7(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-vuejs-accessibility: 2.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(globals@17.12.0)(supports-color@10.2.2) transitivePeerDependencies: - '@eslint/json' - '@typescript-eslint/typescript-estree' @@ -7849,20 +7856,20 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.9 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7887,19 +7894,19 @@ snapshots: '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -7928,7 +7935,7 @@ snapshots: '@babel/parser': 7.29.9 '@babel/types': 7.29.8 - '@babel/traverse@7.29.8': + '@babel/traverse@7.29.8(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -7936,7 +7943,7 @@ snapshots: '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/types': 7.29.8 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -8152,6 +8159,8 @@ snapshots: '@csstools/css-tokenizer@4.0.1': {} + '@csstools/css-tokenizer@4.0.2': {} + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) @@ -8196,13 +8205,13 @@ snapshots: '@dprint/toml@0.7.0': {} - '@e18e/eslint-plugin@0.8.1(eslint@10.11.0(jiti@2.7.0))': + '@e18e/eslint-plugin@0.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': dependencies: empathic: 2.1.0 module-replacements: 3.4.0 verkit: 0.4.1 optionalDependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) '@emnapi/core@1.9.2': dependencies: @@ -8323,115 +8332,115 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.8.1(eslint@10.11.0(jiti@2.7.0))': + '@eslint-community/eslint-plugin-eslint-comments@4.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': dependencies: escape-string-regexp: 4.0.0 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ignore: 7.0.10 - '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/ast@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) string-ts: 2.3.1 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/core@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/core@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.70.1 '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/eslint-plugin@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/eslint-plugin@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) - eslint-plugin-react-dom: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-react-jsx: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-react-naming-convention: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-react-rsc: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-react-web-api: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-react-x: 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-plugin-react-dom: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-react-jsx: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-react-naming-convention: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-react-rsc: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-react-web-api: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-react-x: 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/eslint@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/eslint@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/jsx@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/jsx@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/shared@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/shared@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 zod: 4.6.5 transitivePeerDependencies: - supports-color - '@eslint-react/var@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@eslint-react/var@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.70.1 '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint/compat@2.1.1(eslint@10.11.0(jiti@2.7.0))': + '@eslint/compat@2.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': dependencies: '@eslint/core': 1.2.1 optionalDependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) - '@eslint/config-array@0.23.5': + '@eslint/config-array@0.23.5(supports-color@10.2.2)': dependencies: '@eslint/object-schema': 3.0.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 10.2.6 transitivePeerDependencies: - supports-color @@ -8453,15 +8462,15 @@ snapshots: mdn-data: 2.35.0 source-map-js: 1.2.1 - '@eslint/markdown@8.0.3': + '@eslint/markdown@8.0.3(supports-color@10.2.2)': dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.3 github-slugger: 2.0.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-frontmatter: 2.0.1 - mdast-util-gfm: 3.1.0 - mdast-util-math: 3.0.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-frontmatter: 2.0.1(supports-color@10.2.2) + mdast-util-gfm: 3.1.0(supports-color@10.2.2) + mdast-util-math: 3.0.0(supports-color@10.2.2) micromark-extension-frontmatter: 2.0.0 micromark-extension-gfm: 3.0.0 micromark-extension-math: 3.1.0 @@ -8500,25 +8509,25 @@ snapshots: '@commitlint/types': 21.2.3 conventional-changelog-conventionalcommits: 10.4.0 - '@icebreakers/eslint-config@8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1)': + '@icebreakers/eslint-config@8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(globals@17.12.0)(supports-color@10.2.2))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1)': dependencies: - '@antfu/eslint-config': 9.5.1(@eslint-react/eslint-plugin@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.43)(eslint-plugin-format@2.0.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-react-refresh@0.5.7(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1) - '@eslint-react/eslint-plugin': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@icebreakers/stylelint-config': 5.1.2(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3) - '@typescript-eslint/rule-tester': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@antfu/eslint-config': 9.5.1(058b4d66dc2f2f993468ce5fdf26363b) + '@eslint-react/eslint-plugin': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@icebreakers/stylelint-config': 5.1.2(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3) + '@typescript-eslint/rule-tester': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@vue/compiler-sfc': 3.5.43 - '@weapp-vite/eslint': 0.2.7(eslint@10.11.0(jiti@2.7.0)) + '@weapp-vite/eslint': 0.2.7(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) defu: 6.1.7 eslint-flat-config-utils: 3.2.0 - eslint-plugin-better-tailwindcss: 4.7.0(eslint@10.11.0(jiti@2.7.0))(tailwindcss@4.3.3)(typescript@6.0.3) - eslint-plugin-format: 2.0.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-react-hooks: 7.1.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-react-refresh: 0.5.7(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-tailwindcss: 4.4.0(eslint@10.11.0(jiti@2.7.0))(tailwindcss@4.3.3)(typescript@6.0.3) + eslint-plugin-better-tailwindcss: 4.7.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(tailwindcss@4.3.3)(typescript@6.0.3) + eslint-plugin-format: 2.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-hooks: 7.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-refresh: 0.5.7(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-tailwindcss: 4.4.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) optionalDependencies: - eslint-plugin-pnpm: 1.9.1(eslint@10.11.0(jiti@2.7.0)) + eslint-plugin-pnpm: 1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) tailwindcss: 4.3.3 transitivePeerDependencies: - '@angular-eslint/eslint-plugin' @@ -8562,17 +8571,17 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@icebreakers/monorepo@5.5.5(@commitlint/cli@21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@types/node@26.6.2)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vitest/coverage-v8@5.0.1)(conventional-changelog-conventionalcommits@10.4.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(magicast@0.5.5)(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typanion@3.14.0)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1))': + '@icebreakers/monorepo@5.5.5(09f9eb54c665d2789192a78e0a7ec2d8)': dependencies: '@icebreakers/commitlint-config': 4.1.1(conventional-changelog-conventionalcommits@10.4.0) - '@icebreakers/eslint-config': 8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1) + '@icebreakers/eslint-config': 8.0.2(@next/eslint-plugin-next@16.2.4)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(globals@17.12.0)(supports-color@10.2.2))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3)(vitest@5.0.1) '@icebreakers/monorepo-templates': 2.0.2(@types/node@26.6.2) - '@icebreakers/stylelint-config': 5.1.2(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3) + '@icebreakers/stylelint-config': 5.1.2(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3) '@pnpm/find-workspace-dir': 1000.1.5 '@pnpm/logger': 1100.0.0 '@pnpm/types': 1102.1.0 '@pnpm/worker': 1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2) - '@pnpm/workspace.find-packages': 1000.0.65(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/workspace.find-packages': 1000.0.65(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/workspace.read-manifest': 1000.3.1 c12: 3.3.4(magicast@0.5.5) commander: 15.0.0 @@ -8585,13 +8594,13 @@ snapshots: pathe: 2.0.3 picocolors: 1.1.1 semver: 7.8.5 - simple-git: 3.36.0 + simple-git: 3.36.0(supports-color@10.2.2) vitest: 5.0.1(@types/node@26.6.2)(@vitest/coverage-v8@5.0.1)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) yaml: 2.9.1 optionalDependencies: '@commitlint/cli': 21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) - stylelint: 17.15.0(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - '@angular-eslint/eslint-plugin' @@ -8639,17 +8648,17 @@ snapshots: - typanion - vite - '@icebreakers/stylelint-config@5.1.2(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)': + '@icebreakers/stylelint-config@5.1.2(postcss@8.5.28)(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3)': dependencies: comment-json: 5.0.0 postcss-html: 2.0.0(postcss@8.5.28) - stylelint-config-recess-order: 7.8.0(stylelint-order@8.1.1(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-recommended-vue: 2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)))(stylelint-config-recommended@18.0.0(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-standard-scss: 17.0.0(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)) - stylelint-order: 8.1.1(stylelint@17.15.0(typescript@6.0.3)) - stylelint-plugin-tailwindcss: 3.1.2(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3) + stylelint-config-recess-order: 7.8.0(stylelint-order@8.1.1(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-recommended-vue: 2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint-config-recommended@18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-standard-scss: 17.0.0(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-order: 8.1.1(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-plugin-tailwindcss: 3.1.2(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3) transitivePeerDependencies: - postcss - stylelint @@ -8873,9 +8882,9 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@kwsites/file-exists@1.1.1': + '@kwsites/file-exists@1.1.1(supports-color@10.2.2)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -8926,13 +8935,13 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.3 - '@npmcli/agent@3.0.0': + '@npmcli/agent@3.0.0(supports-color@10.2.2)': dependencies: agent-base: 7.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@10.2.2) + https-proxy-agent: 7.0.6(supports-color@10.2.2) lru-cache: 10.4.3 - socks-proxy-agent: 8.0.5 + socks-proxy-agent: 8.0.5(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -9027,11 +9036,11 @@ snapshots: '@pnpm/types': 1001.3.0 load-json-file: 6.2.0 - '@pnpm/cli-utils@1001.3.10(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/cli-utils@1001.3.10(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/cli-meta': 1000.0.16 '@pnpm/config': 1004.11.0(@pnpm/logger@1100.0.0) - '@pnpm/config.deps-installer': 1000.1.5(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) + '@pnpm/config.deps-installer': 1000.1.5(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2) '@pnpm/default-reporter': 1002.1.14(@pnpm/logger@1100.0.0) '@pnpm/error': 1000.1.0 '@pnpm/logger': 1100.0.0 @@ -9039,7 +9048,7 @@ snapshots: '@pnpm/package-is-installable': 1000.0.21(@pnpm/logger@1100.0.0) '@pnpm/pnpmfile': 1002.1.13(@pnpm/logger@1100.0.0) '@pnpm/read-project-manifest': 1001.2.6(@pnpm/logger@1100.0.0) - '@pnpm/store-connection-manager': 1002.3.19(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/store-connection-manager': 1002.3.19(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/types': 1001.3.0 '@pnpm/util.lex-comparator': 3.0.2 chalk: 4.1.2 @@ -9050,18 +9059,18 @@ snapshots: - supports-color - typanion - '@pnpm/client@1001.1.24(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/client@1001.1.24(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: - '@pnpm/default-resolver': 1002.3.8(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/default-resolver': 1002.3.8(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/directory-fetcher': 1000.1.24(@pnpm/logger@1100.0.0) - '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0) + '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/fetching-types': 1000.2.1 '@pnpm/fetching.binary-fetcher': 1005.0.4(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) - '@pnpm/git-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/git-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/network.auth-header': 1000.0.7 - '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/resolver-base': 1005.4.1 - '@pnpm/tarball-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/tarball-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/types': 1001.3.0 ramda: '@pnpm/ramda@0.28.1' transitivePeerDependencies: @@ -9080,12 +9089,12 @@ snapshots: transitivePeerDependencies: - '@pnpm/logger' - '@pnpm/config.deps-installer@1000.1.5(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))': + '@pnpm/config.deps-installer@1000.1.5(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)': dependencies: '@pnpm/config.config-writer': 1000.1.3(@pnpm/logger@1100.0.0) '@pnpm/core-loggers': 1001.0.9(@pnpm/logger@1100.0.0) '@pnpm/error': 1000.1.0 - '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0) + '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/logger': 1100.0.0 '@pnpm/network.auth-header': 1000.0.7 '@pnpm/npm-resolver': 1005.2.3(@pnpm/logger@1100.0.0) @@ -9218,17 +9227,17 @@ snapshots: stacktracey: 2.2.0 string-length: 4.0.2 - '@pnpm/default-resolver@1002.3.8(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/default-resolver@1002.3.8(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/error': 1000.1.0 '@pnpm/fetching-types': 1000.2.1 - '@pnpm/git-resolver': 1001.2.2(@pnpm/logger@1100.0.0) + '@pnpm/git-resolver': 1001.2.2(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/local-resolver': 1002.1.13(@pnpm/logger@1100.0.0) '@pnpm/node.resolver': 1001.0.23(@pnpm/logger@1100.0.0) '@pnpm/npm-resolver': 1005.2.3(@pnpm/logger@1100.0.0) '@pnpm/resolver-base': 1005.4.1 - '@pnpm/resolving.bun-resolver': 1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) - '@pnpm/resolving.deno-resolver': 1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/resolving.bun-resolver': 1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) + '@pnpm/resolving.deno-resolver': 1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/tarball-resolver': 1002.2.1 transitivePeerDependencies: - '@pnpm/logger' @@ -9271,12 +9280,12 @@ snapshots: dependencies: '@pnpm/types': 1001.3.0 - '@pnpm/fetch@1001.0.0(@pnpm/logger@1100.0.0)': + '@pnpm/fetch@1001.0.0(@pnpm/logger@1100.0.0)(supports-color@10.2.2)': dependencies: '@pnpm/core-loggers': 1001.0.9(@pnpm/logger@1100.0.0) '@pnpm/fetching-types': 1000.2.1 '@pnpm/logger': 1100.0.0 - '@pnpm/network.agent': 2.0.3 + '@pnpm/network.agent': 2.0.3(supports-color@10.2.2) '@pnpm/types': 1001.3.0 '@zkochan/retry': 0.2.0 node-fetch: '@pnpm/node-fetch@1.0.0' @@ -9384,13 +9393,13 @@ snapshots: symlink-dir: 10.0.3 validate-npm-package-name: 7.0.2 - '@pnpm/git-fetcher@1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/git-fetcher@1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/error': 1000.1.0 '@pnpm/fetcher-base': 1001.2.2 '@pnpm/fs.packlist': 1000.0.0 '@pnpm/logger': 1100.0.0 - '@pnpm/prepare-package': 1001.0.7(@pnpm/logger@1100.0.0)(typanion@3.14.0) + '@pnpm/prepare-package': 1001.0.7(@pnpm/logger@1100.0.0)(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/worker': 1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2) '@zkochan/rimraf': 3.0.2 execa: safe-execa@0.1.2 @@ -9398,10 +9407,10 @@ snapshots: - supports-color - typanion - '@pnpm/git-resolver@1001.2.2(@pnpm/logger@1100.0.0)': + '@pnpm/git-resolver@1001.2.2(@pnpm/logger@1100.0.0)(supports-color@10.2.2)': dependencies: '@pnpm/error': 1000.1.0 - '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0) + '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/resolver-base': 1005.4.1 graceful-git: 4.0.0 hosted-git-info: '@pnpm/hosted-git-info@1.0.0' @@ -9428,14 +9437,14 @@ snapshots: dependencies: lru-cache: 6.0.0 - '@pnpm/lifecycle@1001.0.37(@pnpm/logger@1100.0.0)(typanion@3.14.0)': + '@pnpm/lifecycle@1001.0.37(@pnpm/logger@1100.0.0)(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/core-loggers': 1001.0.9(@pnpm/logger@1100.0.0) '@pnpm/directory-fetcher': 1000.1.24(@pnpm/logger@1100.0.0) '@pnpm/error': 1000.1.0 '@pnpm/link-bins': 1000.3.8(@pnpm/logger@1100.0.0) '@pnpm/logger': 1100.0.0 - '@pnpm/npm-lifecycle': 1001.0.0(typanion@3.14.0) + '@pnpm/npm-lifecycle': 1001.0.0(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/read-package-json': 1000.1.8 '@pnpm/store-controller-types': 1004.5.1 '@pnpm/types': 1001.3.0 @@ -9502,10 +9511,10 @@ snapshots: dependencies: escape-string-regexp: 4.0.0 - '@pnpm/network.agent@2.0.3': + '@pnpm/network.agent@2.0.3(supports-color@10.2.2)': dependencies: '@pnpm/network.config': 2.1.0 - '@pnpm/network.proxy-agent': 2.0.3 + '@pnpm/network.proxy-agent': 2.0.3(supports-color@10.2.2) agentkeepalive: 4.6.0 lru-cache: 7.18.3 transitivePeerDependencies: @@ -9524,13 +9533,13 @@ snapshots: dependencies: '@pnpm/config.nerf-dart': 1.0.1 - '@pnpm/network.proxy-agent@2.0.3': + '@pnpm/network.proxy-agent@2.0.3(supports-color@10.2.2)': dependencies: '@pnpm/error': 1000.1.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@10.2.2) + https-proxy-agent: 7.0.6(supports-color@10.2.2) lru-cache: 7.18.3 - socks-proxy-agent: 8.0.5 + socks-proxy-agent: 8.0.5(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -9541,7 +9550,7 @@ snapshots: transitivePeerDependencies: - domexception - '@pnpm/node.fetcher@1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/node.fetcher@1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/create-cafs-store': 1000.0.33(@pnpm/logger@1100.0.0) '@pnpm/crypto.shasums-file': 1001.0.5 @@ -9549,7 +9558,7 @@ snapshots: '@pnpm/fetching-types': 1000.2.1 '@pnpm/fetching.binary-fetcher': 1005.0.4(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) '@pnpm/node.resolver': 1001.0.23(@pnpm/logger@1100.0.0) - '@pnpm/tarball-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/tarball-fetcher': 1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) detect-libc: 2.1.2 transitivePeerDependencies: - '@pnpm/logger' @@ -9579,13 +9588,13 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@pnpm/npm-lifecycle@1001.0.0(typanion@3.14.0)': + '@pnpm/npm-lifecycle@1001.0.0(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/byline': 1.0.0 '@pnpm/error': 1000.1.0 '@yarnpkg/fslib': 3.1.5 '@yarnpkg/shell': 4.0.0(typanion@3.14.0) - node-gyp: 11.5.0 + node-gyp: 11.5.0(supports-color@10.2.2) resolve-from: 5.0.0 slide: 1.1.6 uid-number: 0.0.6 @@ -9731,10 +9740,10 @@ snapshots: chalk: 4.1.2 path-absolute: 1.0.1 - '@pnpm/prepare-package@1001.0.7(@pnpm/logger@1100.0.0)(typanion@3.14.0)': + '@pnpm/prepare-package@1001.0.7(@pnpm/logger@1100.0.0)(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/error': 1000.1.0 - '@pnpm/lifecycle': 1001.0.37(@pnpm/logger@1100.0.0)(typanion@3.14.0) + '@pnpm/lifecycle': 1001.0.37(@pnpm/logger@1100.0.0)(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/read-package-json': 1000.1.8 '@pnpm/types': 1001.3.0 '@zkochan/rimraf': 3.0.2 @@ -9802,7 +9811,7 @@ snapshots: dependencies: '@pnpm/types': 1001.3.0 - '@pnpm/resolving.bun-resolver@1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/resolving.bun-resolver@1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/constants': 1001.3.1 '@pnpm/crypto.shasums-file': 1001.0.5 @@ -9810,7 +9819,7 @@ snapshots: '@pnpm/fetcher-base': 1001.2.2 '@pnpm/fetching-types': 1000.2.1 '@pnpm/fetching.binary-fetcher': 1005.0.4(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) - '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/npm-resolver': 1005.2.3(@pnpm/logger@1100.0.0) '@pnpm/resolver-base': 1005.4.1 '@pnpm/types': 1001.3.0 @@ -9823,7 +9832,7 @@ snapshots: - supports-color - typanion - '@pnpm/resolving.deno-resolver@1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/resolving.deno-resolver@1005.0.11(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/constants': 1001.3.1 '@pnpm/crypto.shasums-file': 1001.0.5 @@ -9831,7 +9840,7 @@ snapshots: '@pnpm/fetcher-base': 1001.2.2 '@pnpm/fetching-types': 1000.2.1 '@pnpm/fetching.binary-fetcher': 1005.0.4(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) - '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/node.fetcher': 1001.0.27(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/npm-resolver': 1005.2.3(@pnpm/logger@1100.0.0) '@pnpm/resolver-base': 1005.4.1 '@pnpm/types': 1001.3.0 @@ -9856,9 +9865,9 @@ snapshots: dependencies: semver: 7.8.5 - '@pnpm/server@1001.0.20(@pnpm/logger@1100.0.0)': + '@pnpm/server@1001.0.20(@pnpm/logger@1100.0.0)(supports-color@10.2.2)': dependencies: - '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0) + '@pnpm/fetch': 1001.0.0(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/logger': 1100.0.0 '@pnpm/store-controller-types': 1004.5.1 '@pnpm/types': 1001.3.0 @@ -9869,15 +9878,15 @@ snapshots: - domexception - supports-color - '@pnpm/store-connection-manager@1002.3.19(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/store-connection-manager@1002.3.19(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/cli-meta': 1000.0.16 - '@pnpm/client': 1001.1.24(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/client': 1001.1.24(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/config': 1004.11.0(@pnpm/logger@1100.0.0) '@pnpm/error': 1000.1.0 '@pnpm/logger': 1100.0.0 '@pnpm/package-store': 1007.1.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2)) - '@pnpm/server': 1001.0.20(@pnpm/logger@1100.0.0) + '@pnpm/server': 1001.0.20(@pnpm/logger@1100.0.0)(supports-color@10.2.2) '@pnpm/store-path': 1000.0.6 '@zkochan/diable': 1.0.2 delay: 5.0.0 @@ -9957,7 +9966,7 @@ snapshots: '@pnpm/error': 1100.1.4 msgpackr: 2.1.0 - '@pnpm/tarball-fetcher@1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/tarball-fetcher@1006.0.6(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: '@pnpm/core-loggers': 1001.0.9(@pnpm/logger@1100.0.0) '@pnpm/error': 1000.1.0 @@ -9966,7 +9975,7 @@ snapshots: '@pnpm/fs.packlist': 1000.0.0 '@pnpm/graceful-fs': 1000.1.0 '@pnpm/logger': 1100.0.0 - '@pnpm/prepare-package': 1001.0.7(@pnpm/logger@1100.0.0)(typanion@3.14.0) + '@pnpm/prepare-package': 1001.0.7(@pnpm/logger@1100.0.0)(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/types': 1001.3.0 '@pnpm/worker': 1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2) '@zkochan/retry': 0.2.0 @@ -10023,9 +10032,9 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@pnpm/workspace.find-packages@1000.0.65(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0)': + '@pnpm/workspace.find-packages@1000.0.65(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0)': dependencies: - '@pnpm/cli-utils': 1001.3.10(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(typanion@3.14.0) + '@pnpm/cli-utils': 1001.3.10(@pnpm/logger@1100.0.0)(@pnpm/worker@1100.4.3(@pnpm/logger@1100.0.0)(@types/node@26.6.2))(supports-color@10.2.2)(typanion@3.14.0) '@pnpm/constants': 1001.3.1 '@pnpm/fs.find-packages': 1000.0.24(@pnpm/logger@1100.0.0) '@pnpm/logger': 1100.0.0 @@ -10364,11 +10373,11 @@ snapshots: '@speed-highlight/core@1.2.24': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@typescript-eslint/types': 8.70.1 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -10497,15 +10506,15 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.70.1 - '@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.70.1 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ignore: 7.0.10 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -10513,34 +10522,34 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.70.1 '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.70.1 - debug: 4.4.3 - eslint: 10.11.0(jiti@2.7.0) + debug: 4.4.3(supports-color@10.2.2) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.70.1(typescript@6.0.3)': + '@typescript-eslint/project-service@8.70.1(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/rule-tester@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/rule-tester@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) ajv: 6.15.0 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 semver: 7.8.5 @@ -10557,13 +10566,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - debug: 4.4.3 - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + debug: 4.4.3(supports-color@10.2.2) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -10571,13 +10580,13 @@ snapshots: '@typescript-eslint/types@8.70.1': {} - '@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.70.1(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.70.1(typescript@6.0.3) + '@typescript-eslint/project-service': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 '@typescript-eslint/visitor-keys': 8.70.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 10.2.6 semver: 7.8.5 tinyglobby: 0.2.17 @@ -10586,13 +10595,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.70.1 '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -10624,9 +10633,9 @@ snapshots: '@unocss/core@66.6.8': optional: true - '@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)': + '@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@unocss/config': 66.6.8 '@unocss/core': 66.6.8 '@unocss/rule-utils': 66.6.8 @@ -10665,13 +10674,13 @@ snapshots: tinyrainbow: 3.1.1 vitest: 5.0.1(@types/node@26.6.2)(@vitest/coverage-v8@5.0.1)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) - '@vitest/eslint-plugin@1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3)(vitest@5.0.1)': + '@vitest/eslint-plugin@1.6.27(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@5.0.1)': dependencies: '@typescript-eslint/scope-manager': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) typescript: 6.0.3 vitest: 5.0.1(@types/node@26.6.2)(@vitest/coverage-v8@5.0.1)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) transitivePeerDependencies: @@ -10796,9 +10805,9 @@ snapshots: transitivePeerDependencies: - typescript - '@weapp-vite/eslint@0.2.7(eslint@10.11.0(jiti@2.7.0))': + '@weapp-vite/eslint@0.2.7(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))': optionalDependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) '@yarnpkg/fslib@3.1.5': dependencies: @@ -11465,9 +11474,11 @@ snapshots: is-data-view: 1.0.2 optional: true - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 decamelize-keys@1.1.1: dependencies: @@ -11748,15 +11759,15 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.5.1(eslint@10.11.0(jiti@2.7.0)): + eslint-compat-utils@0.5.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) semver: 7.8.5 - eslint-config-flat-gitignore@2.4.0(eslint@10.11.0(jiti@2.7.0)): + eslint-config-flat-gitignore@2.4.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@eslint/compat': 2.1.1(eslint@10.11.0(jiti@2.7.0)) - eslint: 10.11.0(jiti@2.7.0) + '@eslint/compat': 2.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-flat-config-utils@3.2.0: dependencies: @@ -11774,28 +11785,28 @@ snapshots: string-width: 4.2.3 supports-hyperlinks: 2.3.0 - eslint-formatting-reporter@0.0.0(eslint@10.11.0(jiti@2.7.0)): + eslint-formatting-reporter@0.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) prettier-linter-helpers: 1.0.1 - eslint-json-compat-utils@0.2.3(eslint@10.11.0(jiti@2.7.0))(jsonc-eslint-parser@3.3.0): + eslint-json-compat-utils@0.2.3(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(jsonc-eslint-parser@3.3.0): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) esquery: 1.7.0 jsonc-eslint-parser: 3.3.0 - eslint-merge-processors@2.0.0(eslint@10.11.0(jiti@2.7.0)): + eslint-merge-processors@2.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-parser-plain@0.1.1: {} - eslint-plugin-antfu@3.2.3(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-antfu@3.2.3(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) - eslint-plugin-better-tailwindcss@4.7.0(eslint@10.11.0(jiti@2.7.0))(tailwindcss@4.3.3)(typescript@6.0.3): + eslint-plugin-better-tailwindcss@4.7.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(tailwindcss@4.3.3)(typescript@6.0.3): dependencies: '@eslint/css-tree': 4.1.1 '@valibot/to-json-schema': 1.8.0(valibot@1.5.0(typescript@6.0.3)) @@ -11807,52 +11818,52 @@ snapshots: tsconfig-paths-webpack-plugin: 4.2.0 valibot: 1.5.0(typescript@6.0.3) optionalDependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) transitivePeerDependencies: - '@eslint/css' - typescript - eslint-plugin-command@4.0.0(@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-command@4.0.0(@typescript-eslint/typescript-estree@8.70.1(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: '@es-joy/jsdoccomment': 0.92.0 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) - eslint-plugin-es-x@7.8.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-es-x@7.8.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - eslint: 10.11.0(jiti@2.7.0) - eslint-compat-utils: 0.5.1(eslint@10.11.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-compat-utils: 0.5.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) - eslint-plugin-format@2.0.1(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-format@2.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: '@dprint/formatter': 0.5.1 '@dprint/markdown': 0.21.1 '@dprint/toml': 0.7.0 - eslint: 10.11.0(jiti@2.7.0) - eslint-formatting-reporter: 0.0.0(eslint@10.11.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-formatting-reporter: 0.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) eslint-parser-plain: 0.1.1 ohash: 2.0.12 oxfmt: 0.35.0 prettier: 3.9.7 synckit: 0.11.13 - eslint-plugin-import-lite@0.6.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-import-lite@0.6.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) - eslint-plugin-jsdoc@64.5.4(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-jsdoc@64.5.4(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: '@es-joy/jsdoccomment': 0.98.0 '@es-joy/resolve.exports': 1.2.0 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) are-docs-informative: 0.1.1 comment-parser: 1.4.9 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) escape-string-regexp: 5.0.0 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) espree: 11.2.0 esquery: 1.7.0 html-entities: 2.6.0 @@ -11866,22 +11877,22 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@3.4.2(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-jsonc@3.4.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.3 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 - eslint: 10.11.0(jiti@2.7.0) - eslint-json-compat-utils: 0.2.3(eslint@10.11.0(jiti@2.7.0))(jsonc-eslint-parser@3.3.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-json-compat-utils: 0.2.3(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(jsonc-eslint-parser@3.3.0) jsonc-eslint-parser: 3.3.0 natural-compare: 1.4.0 synckit: 0.11.13 transitivePeerDependencies: - '@eslint/json' - eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: aria-query: 5.3.2 array-includes: 3.2.0 @@ -11891,7 +11902,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) hasown: 2.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -11901,12 +11912,12 @@ snapshots: string.prototype.includes: 2.0.1 optional: true - eslint-plugin-n@18.3.0(eslint@10.11.0(jiti@2.7.0))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3): + eslint-plugin-n@18.3.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) enhanced-resolve: 5.25.1 - eslint: 10.11.0(jiti@2.7.0) - eslint-plugin-es-x: 7.8.0(eslint@10.11.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-plugin-es-x: 7.8.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) get-tsconfig: 4.14.3 globals: 15.15.0 globrex: 0.1.2 @@ -11918,21 +11929,21 @@ snapshots: eslint-plugin-no-only-tests@3.4.0: {} - eslint-plugin-perfectionist@5.12.0(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-perfectionist@5.12.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: empathic: 2.1.0 - eslint: 10.11.0(jiti@2.7.0) - eslint-json-compat-utils: 0.2.3(eslint@10.11.0(jiti@2.7.0))(jsonc-eslint-parser@3.3.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-json-compat-utils: 0.2.3(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(jsonc-eslint-parser@3.3.0) jsonc-eslint-parser: 3.3.0 pathe: 2.0.3 pnpm-workspace-yaml: 1.9.1 @@ -11942,110 +11953,110 @@ snapshots: transitivePeerDependencies: - '@eslint/json' - eslint-plugin-react-dom@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-dom@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) compare-versions: 6.1.1 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks@7.1.1(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-react-hooks@7.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.9 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) hermes-parser: 0.25.1 zod: 4.6.5 zod-validation-error: 4.0.2(zod@4.6.5) transitivePeerDependencies: - supports-color - eslint-plugin-react-jsx@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-jsx@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-naming-convention@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-naming-convention@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.5.7(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-react-refresh@0.5.7(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) - eslint-plugin-react-rsc@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-rsc@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-web-api@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-web-api@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) ts-pattern: 5.9.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-x@5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-react-x@5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@eslint-react/ast': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/core': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/eslint': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/jsx': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/shared': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint-react/var': 5.20.5(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.70.1 - '@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/types': 8.70.1 - '@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.70.1(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) compare-versions: 6.1.1 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) string-ts: 2.3.1 ts-api-utils: 2.5.0(typescript@6.0.3) ts-pattern: 5.9.0 @@ -12053,21 +12064,21 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-regexp@3.3.1(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-regexp@3.3.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.9 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) jsdoc-type-pratt-parser: 9.2.2 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-tailwindcss@4.4.0(eslint@10.11.0(jiti@2.7.0))(tailwindcss@4.3.3)(typescript@6.0.3): + eslint-plugin-tailwindcss@4.4.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3): dependencies: - '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) + '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) postcss: 8.5.28 postcss-nested: 7.0.2(postcss@8.5.28) synckit: 0.11.13 @@ -12078,20 +12089,20 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-toml@1.5.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-toml@1.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.3 '@ota-meshi/ast-token-store': 0.3.0 - debug: 4.4.3 - eslint: 10.11.0(jiti@2.7.0) + debug: 4.4.3(supports-color@10.2.2) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) toml-eslint-parser: 1.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@74.0.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-unicorn@74.0.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@eslint/css-tree': 4.1.1 browserslist: 4.29.0 change-case: 5.4.4 @@ -12099,7 +12110,7 @@ snapshots: core-js-compat: 3.50.0 detect-indent: 7.0.2 entities: 8.1.0 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) find-up-simple: 1.0.1 globals: 17.12.0 indent-string: 5.0.0 @@ -12113,51 +12124,51 @@ snapshots: strip-indent: 4.1.1 yaml: 2.9.1 - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) - eslint-plugin-vue@10.11.0(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0))): + eslint-plugin-vue@10.11.0(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) - eslint: 10.11.0(jiti@2.7.0) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.6 semver: 7.8.5 - vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)) + vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) xml-name-validator: 5.0.0 optionalDependencies: - '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3) + '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) + '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) - eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0): + eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(globals@17.12.0)(supports-color@10.2.2): dependencies: aria-query: 5.3.2 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) globals: 17.12.0 - vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)) + vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color optional: true - eslint-plugin-yml@3.8.1(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-yml@3.8.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.3 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 - eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.43)(eslint@10.11.0(jiti@2.7.0)): + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.43)(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: '@vue/compiler-sfc': 3.5.43 - eslint: 10.11.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-rule-docs@1.1.235: {} @@ -12174,11 +12185,11 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.11.0(jiti@2.7.0): + eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.5 + '@eslint/config-array': 0.23.5(supports-color@10.2.2) '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.3 @@ -12188,7 +12199,7 @@ snapshots: '@types/estree': 1.0.9 ajv: 6.15.0 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) escape-string-regexp: 4.0.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 @@ -12703,17 +12714,17 @@ snapshots: http-cache-semantics@4.2.0: {} - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -13259,9 +13270,9 @@ snapshots: dependencies: '@zkochan/rimraf': 4.0.0 - make-fetch-happen@14.0.3: + make-fetch-happen@14.0.3(supports-color@10.2.2): dependencies: - '@npmcli/agent': 3.0.0 + '@npmcli/agent': 3.0.0(supports-color@10.2.2) cacache: 19.0.1 http-cache-semantics: 4.2.0 minipass: 7.1.3 @@ -13299,14 +13310,14 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.3: + mdast-util-from-markdown@2.0.3(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.2 + micromark: 4.0.2(supports-color@10.2.2) micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-decode-string: 2.0.1 micromark-util-normalize-identifier: 2.0.1 @@ -13316,12 +13327,12 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-frontmatter@2.0.1: + mdast-util-frontmatter@2.0.1(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 micromark-extension-frontmatter: 2.0.0 transitivePeerDependencies: @@ -13335,62 +13346,62 @@ snapshots: mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.1.0: + mdast-util-gfm-footnote@2.1.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color - mdast-util-gfm-strikethrough@2.0.0: + mdast-util-gfm-strikethrough@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-table@2.0.0: + mdast-util-gfm-table@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-task-list-item@2.0.0: + mdast-util-gfm-task-list-item@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm@3.1.0: + mdast-util-gfm@3.1.0(supports-color@10.2.2): dependencies: - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-gfm-footnote: 2.1.0(supports-color@10.2.2) + mdast-util-gfm-strikethrough: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-table: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-task-list-item: 2.0.0(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-math@3.0.0: + mdast-util-math@3.0.0(supports-color@10.2.2): dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 unist-util-remove-position: 5.0.0 transitivePeerDependencies: @@ -13654,10 +13665,10 @@ snapshots: micromark-util-types@2.0.2: {} - micromark@4.0.2: + micromark@4.0.2(supports-color@10.2.2): dependencies: '@types/debug': 4.1.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -13817,12 +13828,12 @@ snapshots: detect-libc: 2.1.2 optional: true - node-gyp@11.5.0: + node-gyp@11.5.0(supports-color@10.2.2): dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 graceful-fs: 4.2.11 - make-fetch-happen: 14.0.3 + make-fetch-happen: 14.0.3(supports-color@10.2.2) nopt: 8.1.0 proc-log: 5.0.0 semver: 7.8.5 @@ -14432,13 +14443,13 @@ snapshots: '@zkochan/rimraf': 4.0.0 fs-extra: 11.3.4 - repoctl@5.5.5(@commitlint/cli@21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@types/node@26.6.2)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vitest/coverage-v8@5.0.1)(conventional-changelog-conventionalcommits@10.4.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(magicast@0.5.5)(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typanion@3.14.0)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1))(vitest@5.0.1): + repoctl@5.5.5(6902e60b5cf843af70b90e42cb6d7e42): dependencies: - '@icebreakers/monorepo': 5.5.5(@commitlint/cli@21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3))(@next/eslint-plugin-next@16.2.4)(@types/node@26.6.2)(@unocss/eslint-plugin@66.6.8(eslint@10.11.0(jiti@2.7.0))(typescript@6.0.3))(@vitest/coverage-v8@5.0.1)(conventional-changelog-conventionalcommits@10.4.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-pnpm@1.9.1(eslint@10.11.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.5.0(eslint@10.11.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.11.0(jiti@2.7.0))(magicast@0.5.5)(postcss@8.5.28)(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3)(ts-declaration-location@1.0.7(typescript@6.0.3))(typanion@3.14.0)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + '@icebreakers/monorepo': 5.5.5(09f9eb54c665d2789192a78e0a7ec2d8) optionalDependencies: '@commitlint/cli': 21.2.3(@types/node@26.6.2)(conventional-commits-parser@7.1.2)(typescript@6.0.3) - eslint: 10.11.0(jiti@2.7.0) - stylelint: 17.15.0(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) typescript: 6.0.3 vitest: 5.0.1(@types/node@26.6.2)(@vitest/coverage-v8@5.0.1)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) transitivePeerDependencies: @@ -14813,13 +14824,13 @@ snapshots: signal-exit@4.1.0: {} - simple-git@3.36.0: + simple-git@3.36.0(supports-color@10.2.2): dependencies: - '@kwsites/file-exists': 1.1.1 + '@kwsites/file-exists': 1.1.1(supports-color@10.2.2) '@kwsites/promise-deferred': 1.1.1 '@simple-git/args-pathspec': 1.0.3 '@simple-git/argv-parser': 1.1.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -14845,10 +14856,10 @@ snapshots: smart-buffer@4.2.0: {} - socks-proxy-agent@8.0.5: + socks-proxy-agent@8.0.5(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) socks: 2.8.10 transitivePeerDependencies: - supports-color @@ -15019,69 +15030,69 @@ snapshots: strip-indent@4.1.1: {} - stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: postcss-html: 2.0.0(postcss@8.5.28) - stylelint: 17.15.0(typescript@6.0.3) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) - stylelint-config-recess-order@7.8.0(stylelint-order@8.1.1(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-recess-order@7.8.0(stylelint-order@8.1.1(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: - stylelint: 17.15.0(typescript@6.0.3) - stylelint-order: 8.1.1(stylelint@17.15.0(typescript@6.0.3)) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) + stylelint-order: 8.1.1(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) - stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: postcss-scss: 4.0.9(postcss@8.5.28) - stylelint: 17.15.0(typescript@6.0.3) - stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3)) - stylelint-scss: 7.3.0(stylelint@17.15.0(typescript@6.0.3)) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) + stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-scss: 7.3.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) optionalDependencies: postcss: 8.5.28 - stylelint-config-recommended-vue@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint-config-html@1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)))(stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)))(stylelint-config-recommended@18.0.0(stylelint@17.15.0(typescript@6.0.3)))(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-recommended-vue@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint-config-html@2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint-config-recommended-scss@17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint-config-recommended@18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: postcss-html: 2.0.0(postcss@8.5.28) semver: 7.8.5 - stylelint: 17.15.0(typescript@6.0.3) - stylelint-config-html: 1.1.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3)) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) + stylelint-config-html: 2.0.0(postcss-html@2.0.0(postcss@8.5.28))(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) optionalDependencies: - stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)) + stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) - stylelint-config-recommended@18.0.0(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-recommended@18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: - stylelint: 17.15.0(typescript@6.0.3) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) - stylelint-config-standard-scss@17.0.0(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-standard-scss@17.0.0(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: - stylelint: 17.15.0(typescript@6.0.3) - stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(typescript@6.0.3)) - stylelint-config-standard: 40.0.0(stylelint@17.15.0(typescript@6.0.3)) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) + stylelint-config-recommended-scss: 17.0.1(postcss@8.5.28)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) + stylelint-config-standard: 40.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) optionalDependencies: postcss: 8.5.28 - stylelint-config-standard@40.0.0(stylelint@17.15.0(typescript@6.0.3)): + stylelint-config-standard@40.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: - stylelint: 17.15.0(typescript@6.0.3) - stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3)) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) + stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)) - stylelint-order@8.1.1(stylelint@17.15.0(typescript@6.0.3)): + stylelint-order@8.1.1(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: postcss: 8.5.28 postcss-sorting: 10.0.0(postcss@8.5.28) - stylelint: 17.15.0(typescript@6.0.3) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) - stylelint-plugin-tailwindcss@3.1.2(stylelint@17.15.0(typescript@6.0.3))(tailwindcss@4.3.3): + stylelint-plugin-tailwindcss@3.1.2(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(tailwindcss@4.3.3): dependencies: '@unocss/config': 66.10.5 '@unocss/core': 66.10.5 postcss: 8.5.28 postcss-tailwindcss: 3.1.2 - stylelint: 17.15.0(typescript@6.0.3) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) optionalDependencies: tailwindcss: 4.3.3 - stylelint-scss@7.3.0(stylelint@17.15.0(typescript@6.0.3)): + stylelint-scss@7.3.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)): dependencies: '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) @@ -15094,9 +15105,9 @@ snapshots: postcss-resolve-nested-selector: 0.1.6 postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 - stylelint: 17.15.0(typescript@6.0.3) + stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3) - stylelint@17.15.0(typescript@6.0.3): + stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3): dependencies: '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) @@ -15109,7 +15120,7 @@ snapshots: cosmiconfig: 9.0.2(typescript@6.0.3) css-functions-list: 3.3.3 css-tree: 3.2.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fast-glob: 3.3.3 fastest-levenshtein: 1.0.16 file-entry-cache: 11.1.5 @@ -15558,9 +15569,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): + vite-tsconfig-paths@6.1.1(supports-color@10.2.2)(typescript@6.0.3)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) globrex: 0.1.2 tsconfck: 3.1.6(typescript@6.0.3) vite: 8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1) @@ -15674,10 +15685,10 @@ snapshots: transitivePeerDependencies: - msw - vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0)): + vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - debug: 4.4.3 - eslint: 10.11.0(jiti@2.7.0) + debug: 4.4.3(supports-color@10.2.2) + eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2) eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 diff --git a/scripts/check-cjs-plugins.mjs b/scripts/check-cjs-plugins.mjs index 02cdcf6..4c4082e 100644 --- a/scripts/check-cjs-plugins.mjs +++ b/scripts/check-cjs-plugins.mjs @@ -95,12 +95,20 @@ function extractPackage(fixture, name, tarball) { fs.renameSync(join(fixture, 'package'), packageDir) } -function linkRootDependency(fixture, name) { - const target = join(root, 'node_modules', name) - if (!fs.existsSync(target)) { - throw new Error(`Missing workspace dependency ${target}`) +function linkRuntimeDependencies(fixture, name) { + const pkg = packageJson(name) + for (const dependency of Object.keys({ ...pkg.dependencies, ...pkg.peerDependencies })) { + if (packageNames.includes(dependency)) { + continue + } + const target = join(root, 'packages', name, 'node_modules', dependency) + if (!fs.existsSync(target)) { + throw new Error(`Missing runtime dependency ${target}`) + } + const destination = join(fixture, 'node_modules', name, 'node_modules', dependency) + fs.mkdirSync(dirname(destination), { recursive: true }) + fs.symlinkSync(target, destination, 'junction') } - fs.symlinkSync(target, join(fixture, 'node_modules', name), 'junction') } async function main() { @@ -114,10 +122,11 @@ async function main() { extractPackage(fixture, name, packPackage(fixture, name)) } - // Resolve only the packed artifacts from the isolated fixture. The shared - // package still needs its normal runtime dependency, so link that one from - // the workspace without exposing workspace source exports to the test. - linkRootDependency(fixture, 'defu') + // Resolve workspace packages only from their packed artifacts. Link each + // package's declared runtime and peer dependencies without source exports. + for (const name of packageNames) { + linkRuntimeDependencies(fixture, name) + } const fixtureRequire = createRequire(fixtureEntry) const postcss = rootRequire('postcss') From c40357d5e4a06a56eec3c7a31fd1a1151e1eb617 Mon Sep 17 00:00:00 2001 From: ice breaker <1324318532@qq.com> Date: Sun, 11 Oct 2026 19:25:19 +0800 Subject: [PATCH 2/2] fix: accept interface options in typed unit presets --- .../PRESET_AUTHORING.md | 5 +++ .../PRESET_AUTHORING.zh-CN.md | 4 ++ .../src/presets.ts | 5 ++- .../postcss-rule-unit-converter/src/types.ts | 3 +- .../test-d/index.test-d.ts | 44 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md index 908f735..d3f31b3 100644 --- a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md +++ b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.md @@ -22,6 +22,11 @@ Use a preset group when: ## Authoring A Single Preset +Preset options can use named interfaces or object types without an index +signature. `PresetFactory`, `PresetGroupFactory`, `definePreset`, and +`definePresetGroup` share the `object | undefined` constraint; primitive option +types such as `number` are rejected. + ```ts import type { PresetFactory } from 'postcss-rule-unit-converter' import { definePreset } from 'postcss-rule-unit-converter' diff --git a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md index a5443b9..7cd3857 100644 --- a/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md +++ b/packages/postcss-rule-unit-converter/PRESET_AUTHORING.zh-CN.md @@ -22,6 +22,10 @@ ## 编写单条 Preset +Preset options 可以使用命名 interface 或普通对象类型,无需添加索引签名。 +`PresetFactory`、`PresetGroupFactory`、`definePreset` 和 `definePresetGroup` +统一接受 `object | undefined`,不接受 `number` 等原始类型。 + ```ts import type { PresetFactory } from 'postcss-rule-unit-converter' import { definePreset } from 'postcss-rule-unit-converter' diff --git a/packages/postcss-rule-unit-converter/src/presets.ts b/packages/postcss-rule-unit-converter/src/presets.ts index cdea140..b9fad86 100644 --- a/packages/postcss-rule-unit-converter/src/presets.ts +++ b/packages/postcss-rule-unit-converter/src/presets.ts @@ -4,6 +4,7 @@ import type { NumericResolver, PresetFactory, PresetGroupFactory, + PresetOptions, RuleContext, UnitMap, UnitMatcher, @@ -212,7 +213,7 @@ function createUnitTransformRule( /** * Helper for authoring a typed single-rule preset. */ -export function definePreset | undefined = undefined>( +export function definePreset( factory: PresetFactory, ) { return factory @@ -221,7 +222,7 @@ export function definePreset | undefine /** * Helper for authoring a typed grouped preset. */ -export function definePresetGroup | undefined = undefined>( +export function definePresetGroup( factory: PresetGroupFactory, ) { return factory diff --git a/packages/postcss-rule-unit-converter/src/types.ts b/packages/postcss-rule-unit-converter/src/types.ts index 0d4491a..ebfab86 100644 --- a/packages/postcss-rule-unit-converter/src/types.ts +++ b/packages/postcss-rule-unit-converter/src/types.ts @@ -81,7 +81,8 @@ export interface ConversionRule { } export type RuleGroup = ConversionRule | readonly ConversionRule[] -export type PresetOptions = Record | undefined +/** Preset option objects do not require a string index signature. */ +export type PresetOptions = object | undefined export type PresetFactory = ( options?: TOptions, ) => ConversionRule diff --git a/packages/postcss-rule-unit-converter/test-d/index.test-d.ts b/packages/postcss-rule-unit-converter/test-d/index.test-d.ts index ee02b52..d426e9d 100644 --- a/packages/postcss-rule-unit-converter/test-d/index.test-d.ts +++ b/packages/postcss-rule-unit-converter/test-d/index.test-d.ts @@ -2,8 +2,14 @@ import { expectError, expectType } from 'tsd' import type { PluginCreator } from 'postcss' import plugin, { composeRules, + definePreset, + definePresetGroup, presets, type ConversionRule, + type PresetFactory, + type PresetGroupFactory, + type RemBasedPresetOptions, + type RpxPresetGroupOptions, type RuleContext, type UnitTransform, type UserDefinedOptions, @@ -32,3 +38,41 @@ plugin({ mediaQuery: true, exclude: path => path.includes('vendor'), }) + +const remFactory: PresetFactory = definePreset( + (options = {}) => presets.remToPx(options), +) +expectType(remFactory({ rootValue: 20 })) +expectError(remFactory({ rootValue: '20' })) + +const rpxFactory: PresetGroupFactory = definePresetGroup( + (options = {}) => presets.rpxPresetGroup(options), +) +expectType(rpxFactory({ ratio: 2, viewportWidth: 375 })) +expectError(rpxFactory({ ratio: '2' })) + +interface CustomPresetOptions { + factor?: number + unit?: string +} +const customFactory = definePreset((options: CustomPresetOptions = {}) => ({ + from: options.unit ?? 'foo', + to: 'px', + factor: options.factor ?? 2, +})) +expectType>(customFactory) +expectType(customFactory({ factor: 4 })) +const customGroup = definePresetGroup((options: CustomPresetOptions = {}) => [customFactory(options)]) +expectType>(customGroup) +expectType(customGroup({ unit: 'bar' })) +expectError(customFactory({ factor: '4' })) +expectError(customGroup(4)) + +const literalFactory = definePreset<{ factor?: number }>((options = {}) => ({ from: 'foo', to: 'px', factor: options.factor ?? 2 })) +expectType(literalFactory({ factor: 3 })) +const noOptions = definePreset(() => presets.remToPx()) +expectType>(noOptions) +expectType(noOptions()) +expectError(noOptions(2)) +expectError(definePreset(() => presets.remToPx())) +expectError(definePresetGroup(() => [presets.remToPx()]))