",
"license": "MIT",
"repository": {
@@ -86,9 +72,7 @@
},
"peerDependencies": {
"@tailwindcss/postcss": "^4.1.10",
- "react": ">=16.8.0",
- "tailwindcss": "^4.1.10",
- "postcss": "^8.5.5"
+ "react": ">=16.8.0"
},
"dependencies": {
"@babel/code-frame": "^7.27.1",
diff --git a/packages/core/src/cli/postInstall.ts b/packages/core/src/cli/postInstall.ts
index d8aeef3..34cadbe 100644
--- a/packages/core/src/cli/postInstall.ts
+++ b/packages/core/src/cli/postInstall.ts
@@ -4,6 +4,13 @@ import { generateAttributesFile, patchTsConfig, patchPostcssConfig, patchViteCon
import { processVariants } from '../postcss/ast-parsing.js';
export async function runZeroUiInit() {
+ // 0️⃣ Check peer dependency
+ try {
+ require.resolve('@tailwindcss/postcss');
+ } catch {
+ console.error('\n[Zero-UI] ❌ Missing peer dependency "@tailwindcss/postcss".\n' + 'Run: npm install @tailwindcss/postcss\n');
+ process.exit(1);
+ }
try {
console.log('[Zero-UI] Initializing...');
diff --git a/packages/core/src/postcss/ast-parsing.test.ts b/packages/core/src/postcss/ast-parsing.test.ts
index ce8eeb7..90f9eef 100644
--- a/packages/core/src/postcss/ast-parsing.test.ts
+++ b/packages/core/src/postcss/ast-parsing.test.ts
@@ -101,3 +101,26 @@ test('collectUseUIHooks handles + both hooks', async () => {
]
);
});
+
+test('collectUseUIHooks NumericLiterals bound to const identifiers', async () => {
+ const code = `
+ import { useUI, useScopedUI } from '@react-zero-ui/core';
+ const T = "true"; const M = { "true": "dark", "false": "light" }; const x = M[T]
+ export function Comp() {
+ const [, setTheme] = useUI<'theme', 'dark'>('theme', x);
+ const [, setAcc] = useScopedUI<'accordion', 'closed'>('accordion','closed');
+ return
;
+ }
+ `;
+ const ast = parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
+ const hooks = collectUseUIHooks(ast, code);
+
+ assert.equal(hooks.length, 2);
+ assert.deepEqual(
+ hooks.map((h) => [h.stateKey, h.scope]),
+ [
+ ['theme', 'global'],
+ ['accordion', 'scoped'],
+ ]
+ );
+});
diff --git a/packages/core/src/postcss/ast-parsing.ts b/packages/core/src/postcss/ast-parsing.ts
index cce68e4..fcb5f51 100644
--- a/packages/core/src/postcss/ast-parsing.ts
+++ b/packages/core/src/postcss/ast-parsing.ts
@@ -41,8 +41,10 @@ export interface HookMeta {
* reduced to a space-free string.
*/
const ALL_HOOK_NAMES = new Set([CONFIG.HOOK_NAME, CONFIG.LOCAL_HOOK_NAME]);
+type HookName = typeof CONFIG.HOOK_NAME | typeof CONFIG.LOCAL_HOOK_NAME;
const OBJ_NAMES = new Set([CONFIG.SSR_HOOK_NAME, CONFIG.SSR_HOOK_NAME_SCOPED]);
+type LocalHookName = typeof CONFIG.SSR_HOOK_NAME | typeof CONFIG.SSR_HOOK_NAME_SCOPED;
export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
const hooks: HookMeta[] = [];
@@ -51,7 +53,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
const optsBase = { throwOnFail: true, source: sourceCode } as ResolveOpts;
- function lit(node: t.Expression, p: NodePath
, hook: ResolveOpts['hook']): string | null {
+ function resolveLiteralMemoized(node: t.Expression, p: NodePath, hook: ResolveOpts['hook']): string | null {
if (memo.has(node)) return memo.get(node)!;
// clone instead of mutate
@@ -75,7 +77,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
if (!t.isArrayPattern(id) || !t.isCallExpression(init)) return;
// b) callee must be one of our hook names
- if (!(t.isIdentifier(init.callee) && ALL_HOOK_NAMES.has(init.callee.name as any))) return;
+ if (!(t.isIdentifier(init.callee) && ALL_HOOK_NAMES.has(init.callee.name as HookName))) return;
if (id.elements.length !== 2) {
throwCodeFrame(path, path.opts?.filename, sourceCode, `[Zero-UI] useUI() must destructure two values: [value, setterFn].`);
@@ -90,7 +92,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
const [keyArg, initialArg] = init.arguments;
// resolve state key with new helpers
- const stateKey = lit(keyArg as t.Expression, path as NodePath, 'stateKey');
+ const stateKey = resolveLiteralMemoized(keyArg as t.Expression, path as NodePath, 'stateKey');
if (stateKey === null) {
throwCodeFrame(
@@ -103,7 +105,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
}
// resolve initial value with helpers
- const initialValue = lit(initialArg as t.Expression, path as NodePath, 'initialValue');
+ const initialValue = resolveLiteralMemoized(initialArg as t.Expression, path as NodePath, 'initialValue');
if (initialValue === null) {
throwCodeFrame(
@@ -134,7 +136,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
if (
!t.isMemberExpression(callee) ||
!t.isIdentifier(callee.object) ||
- !OBJ_NAMES.has(callee.object.name as any) ||
+ !OBJ_NAMES.has(callee.object.name as LocalHookName) ||
!t.isIdentifier(callee.property, { name: 'onClick' })
)
return;
@@ -144,7 +146,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
const [keyArg, arrArg] = path.node.arguments;
/* --- resolve key ------------------------------------------------ */
- const stateKey = lit(keyArg as t.Expression, path, 'stateKey');
+ const stateKey = resolveLiteralMemoized(keyArg as t.Expression, path, 'stateKey');
if (stateKey === null) {
throwCodeFrame(keyArg, path.opts?.filename, sourceCode, `[Zero-UI] zeroSSR.onClick("key"): key must be a fully-static string.`);
}
@@ -160,7 +162,7 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
}
const values: string[] = [];
for (const el of arrArg.elements) {
- const v = lit(el as t.Expression, path, 'initialValue');
+ const v = resolveLiteralMemoized(el as t.Expression, path, 'initialValue');
if (v === null) {
throwCodeFrame(el!, path.opts?.filename, sourceCode, `[Zero-UI] zeroSSR.onClick("${stateKey}",[string]): array values must be static strings.`);
}
diff --git a/packages/core/src/postcss/index.cts b/packages/core/src/postcss/index.cts
index 72ccd88..7636921 100644
--- a/packages/core/src/postcss/index.cts
+++ b/packages/core/src/postcss/index.cts
@@ -4,14 +4,15 @@
*/
import { buildCss, generateAttributesFile, isZeroUiInitialized } from './helpers';
import { runZeroUiInit } from '../cli/postInstall.js';
-import type { PluginCreator, Root, Result } from 'postcss';
import { processVariants } from './ast-parsing';
import { CONFIG } from '../config';
-import { formatError, registerDeps } from './utilities.js';
+import { formatError, registerDeps, Result } from './utilities.js';
+
+type Root = { prepend: (css: string) => void };
const zeroUIPlugin = CONFIG.PLUGIN_NAME;
-const plugin: PluginCreator = () => {
+const plugin = () => {
return {
postcssPlugin: zeroUIPlugin,
async Once(root: Root, { result }: { result: Result }) {
diff --git a/packages/core/src/postcss/resolvers.test.ts b/packages/core/src/postcss/resolvers.test.ts
index 2019c37..82dcbdd 100644
--- a/packages/core/src/postcss/resolvers.test.ts
+++ b/packages/core/src/postcss/resolvers.test.ts
@@ -9,15 +9,18 @@ import traverse from './traverse.cjs';
/*
Test Coverage:
-1. literalFromNode (8 tests)
-- String literals
-- Template literals without expressions
-- Binary string concatenation with +
-- Logical OR expressions (||)
-- Nullish coalescing (??)
-- Identifiers bound to const
-- Template literals with expressions
-- Non-literal expressions (returns null)
+1. literalFromNode
+
+ LogicalExpressions
+ ConditionalExpression
+ BinaryExpression
+ UnaryExpression
+ StringLiteral
+ TemplateLiteral
+ Identifier
+ MemberExpression
+ UnaryExpression
+
2. resolveLocalConstIdentifier (6 tests)
- Const string literals
- Const template literals
@@ -40,6 +43,8 @@ Test Coverage:
- Optional member expressions (THEMES?.dark)
- Computed property with const variable
- TypeScript as const assertions
+
+
*/
// Helper to find a specific expression node
@@ -74,6 +79,145 @@ test('literalFromNode should resolve string literals', async () => {
});
});
+test('literalFromNode should resolve UnaryExpression that results in a string', async () => {
+ await runTest({}, async () => {
+ const code = `const x = typeof "hello";`;
+ const found = findExpression(
+ code,
+ (n) => t.isUnaryExpression(n) && n.operator === 'typeof' && t.isStringLiteral(n.argument) && n.argument.value === 'hello'
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, 'string');
+ });
+});
+
+test('literalFromNode should resolve UnaryExpression coerced in TemplateLiteral', async () => {
+ await runTest({}, async () => {
+ const code = 'const x = `${!true}`;';
+ const found = findExpression(
+ code,
+ (n) =>
+ t.isTemplateLiteral(n) &&
+ n.expressions.length === 1 &&
+ t.isUnaryExpression(n.expressions[0]) &&
+ n.expressions[0].operator === '!' &&
+ t.isBooleanLiteral(n.expressions[0].argument) &&
+ n.expressions[0].argument.value === true
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, 'false');
+ });
+});
+
+test('literalFromNode should resolve !false inside TemplateLiteral', async () => {
+ await runTest({}, async () => {
+ const code = 'const x = `flag is: ${!false}`;';
+ const found = findExpression(
+ code,
+ (n) =>
+ t.isTemplateLiteral(n) &&
+ n.expressions.length === 1 &&
+ t.isUnaryExpression(n.expressions[0]) &&
+ n.expressions[0].operator === '!' &&
+ t.isBooleanLiteral(n.expressions[0].argument) &&
+ n.expressions[0].argument.value === false
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, 'flag is: true');
+ });
+});
+
+test('literalFromNode should resolve multiple UnaryExpressions inside TemplateLiteral', async () => {
+ await runTest({}, async () => {
+ const code = 'const x = `${+true}${-2}${typeof null}`;';
+ const found = findExpression(
+ code,
+ (n) =>
+ t.isTemplateLiteral(n) &&
+ n.expressions.length === 3 &&
+ t.isUnaryExpression(n.expressions[0]) &&
+ t.isUnaryExpression(n.expressions[1]) &&
+ t.isUnaryExpression(n.expressions[2])
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, '1-2object');
+ });
+});
+
+test('literalFromNode should resolve typeof null inside TemplateLiteral', async () => {
+ await runTest({}, async () => {
+ const code = 'const x = `type: ${typeof null}`;';
+ const found = findExpression(
+ code,
+ (n) => t.isTemplateLiteral(n) && n.expressions.length === 1 && t.isUnaryExpression(n.expressions[0]) && n.expressions[0].operator === 'typeof'
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, 'type: object');
+ });
+});
+
+test('literalFromNode should resolve !someConst with local const', async () => {
+ await runTest({}, async () => {
+ const code = `
+ const someConst = true;
+ const x = \`prefix-\${!someConst}\`;
+ `;
+ const found = findExpression(
+ code,
+ (n) =>
+ t.isTemplateLiteral(n) &&
+ n.expressions.length === 1 &&
+ t.isUnaryExpression(n.expressions[0]) &&
+ t.isIdentifier(n.expressions[0].argument) &&
+ n.expressions[0].operator === '!'
+ );
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+ assert.strictEqual(result, 'prefix-false');
+ });
+});
+
+test('literalFromNode should resolve UnaryExpressions to strings', async () => {
+ const cases = [
+ { description: 'typeof "hello"', code: `const x = typeof "hello";`, expected: 'string' },
+ { description: '+42 coerced to string', code: 'const x = `${+42}`;', expected: '42' },
+ { description: '-5 coerced to string', code: 'const x = `${-5}`;', expected: '-5' },
+ { description: '!false coerced to string', code: 'const x = `${!false}`;', expected: 'true' },
+ { description: 'void 0 coerced to string', code: 'const x = `${void 0}`;', expected: 'undefined' },
+ ];
+
+ await runTest({}, async () => {
+ for (const testCase of cases) {
+ const { code, expected, description } = testCase;
+
+ const found = findExpression(code, (n) => t.isTemplateLiteral(n) || (t.isUnaryExpression(n) && ['typeof', '+', '-', '!', 'void'].includes(n.operator)));
+ assert(found, `Expected expression for: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node as t.Expression, found.path, opts);
+
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
+ });
+});
+
test('literalFromNode should resolve template literals with no expressions', async () => {
await runTest({}, async () => {
const code = 'const x = `hello world`;';
@@ -86,6 +230,56 @@ test('literalFromNode should resolve template literals with no expressions', asy
});
});
+test('literalFromNode should return null or throw on invalid UnaryExpressions', async () => {
+ const cases = [
+ { description: 'typeof function call (dynamic)', code: `const x = typeof getValue();`, shouldThrow: false },
+ { description: 'typeof runtime identifier (undeclared)', code: `const x = typeof runtimeValue;`, shouldThrow: false },
+ { description: '+imported identifier (illegal)', code: `import { value } from './lib.js'; const x = \`\${+value}\`;`, shouldThrow: true },
+ { description: 'Unary ! with non-const identifier', code: `let flag = true; const x = \`\${!flag}\`;`, shouldThrow: false },
+ { description: 'typeof Symbol() (non-serializable)', code: `const x = \`\${typeof Symbol()}\`;`, shouldThrow: false },
+ ];
+
+ await runTest({}, async () => {
+ for (const testCase of cases) {
+ const { code, description, shouldThrow } = testCase;
+ const found = findExpression(code, (n) => t.isTemplateLiteral(n) || (t.isUnaryExpression(n) && ['typeof', '+', '-', '!', 'void'].includes(n.operator)));
+ assert(found, `Expected expression for: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: shouldThrow, source: code };
+
+ let didThrow = false;
+ let result: string | null = null;
+
+ try {
+ result = literalFromNode(found.node as t.Expression, found.path, opts);
+ } catch (e) {
+ didThrow = true;
+ }
+
+ if (shouldThrow) {
+ assert(didThrow, `Expected to throw for: ${description}`);
+ } else {
+ assert.strictEqual(result, null, `Expected null for: ${description}`);
+ }
+ }
+ });
+});
+
+// Conditional Expression
+test('literalFromNode should handle ConditionalExpression', async () => {
+ await runTest({}, async () => {
+ const code = `
+ const x = true ? "isTrue" : "isFalse";
+ `;
+ const found = findExpression(code, (n) => t.isConditionalExpression(n));
+ assert(found);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node as t.ConditionalExpression, found.path, opts);
+ assert.strictEqual(result, 'isTrue');
+ });
+});
+
test('literalFromNode should resolve binary string concatenation', async () => {
await runTest({}, async () => {
const code = `const x = "hello" + " " + "world";`;
@@ -98,18 +292,31 @@ test('literalFromNode should resolve binary string concatenation', async () => {
});
});
-test('literalFromNode should resolve logical OR expressions', async () => {
+test('literalFromNode should resolve various LogicalExpressions to strings', async () => {
+ const cases = [
+ { description: 'OR: undefined || "default"', code: `const undef=undefined; const x = undef || "default";`, expected: 'default' },
+ { description: 'OR: "primary" || "fallback"', code: `const x = "primary" || "fallback";`, expected: 'primary' },
+ { description: 'OR: null || undefined || "final"', code: `const n=null; const undef=undefined; const x = n || undef || "final";`, expected: 'final' },
+ { description: 'OR: null || fallback identifier', code: `const fallback = "default"; const x = null || fallback;`, expected: 'default' },
+ { description: 'OR: null || fallback identifier', code: `const fallback = "default"; const x = null || fallback;`, expected: 'default' },
+ { description: 'AND: "truthy" && "final"', code: `const x = "truthy" && "final";`, expected: 'final' },
+ { description: 'AND: null && "never hit"', code: `const x = null && "never hit";`, expected: null },
+ { description: 'Nullish: null ?? "default"', code: `const x = null ?? "default";`, expected: 'default' },
+ { description: 'Nullish: "set" ?? "default"', code: `const x = "set" ?? "default";`, expected: 'set' },
+ ];
+
await runTest({}, async () => {
- const code = `
- const fallback = "default";
- const x = undefined || fallback;
- `;
- const found = findExpression(code, (n) => t.isLogicalExpression(n) && n.operator === '||');
- assert(found);
+ for (const testCase of cases) {
+ const { code, expected, description } = testCase;
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = literalFromNode(found.node, found.path, opts);
- assert.strictEqual(result, 'default');
+ const found = findExpression(code, (n) => t.isLogicalExpression(n));
+ assert(found, `Expected to find LogicalExpression in: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node, found.path, opts);
+
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
});
});
@@ -171,6 +378,32 @@ test('literalFromNode should return null for non-literal expressions', async ()
});
});
+test('literalFromNode should resolve ArrayExpression values via static index access', async () => {
+ const cases = [
+ { description: 'array access with numeric index', code: `const COLORS = ["red", "green", "blue"]; const x = COLORS[1];`, expected: 'green' },
+ { description: 'array access with numeric index', code: `const idx = 0; const COLORS = ["red", "green", "blue"]; const x = COLORS[idx];`, expected: 'red' },
+ // { description: 'nested object in array access', code: `const THEMES = [{ name: "light" }, { name: "dark" }]; const x = THEMES[1].name;`, expected: 'dark' },
+ // { description: 'template literal inside array', code: 'const VALUES = [`va${"lue"}`]; const x = VALUES[0];', expected: 'value' },
+ // { description: 'computed numeric index from const', code: `const IDX = 2; const LIST = ["a", "b", 'c']; const x = LIST[IDX];`, expected: 'c' },
+ // { description: 'array inside object', code: `const DATA = { values: ["a", "b", "c"] }; const x = DATA.values[0];`, expected: 'a' },
+ ];
+
+ await runTest({}, async () => {
+ for (const testCase of cases) {
+ const { code, description, expected } = testCase;
+
+ const found = findExpression(code, (n) => t.isMemberExpression(n) || t.isOptionalMemberExpression(n));
+ assert(found, `Expected MemberExpression for: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+ const result = literalFromNode(found.node as t.Expression, found.path, opts);
+ console.log('result: ', result);
+
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
+ });
+});
+
// Tests for resolveLocalConstIdentifier
test('resolveLocalConstIdentifier should resolve const string literals', async () => {
await runTest({}, async () => {
@@ -214,7 +447,7 @@ test('resolveLocalConstIdentifier should return null for non-identifier', async
});
});
-test('resolveLocalConstIdentifier should return null for let/var variables', async () => {
+test('resolveLocalConstIdentifier should throw for let/var variables', async () => {
await runTest({}, async () => {
const code = `
let THEME = "dark";
@@ -224,8 +457,9 @@ test('resolveLocalConstIdentifier should return null for let/var variables', asy
assert(found);
const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveLocalConstIdentifier(found.node, found.path, opts);
- assert.strictEqual(result, null);
+ assert.throws(() => {
+ resolveLocalConstIdentifier(found.node, found.path, opts);
+ }, /Only top-level `const` variables are allowed./);
});
});
@@ -320,146 +554,140 @@ test('resolveTemplateLiteral should throw for dynamic expressions', async () =>
});
// Tests for resolveMemberExpression
-test('resolveMemberExpression should resolve simple object property', async () => {
- await runTest({}, async () => {
- const code = `
- const THEMES = { dark: "dark-theme", light: "light-theme" };
- const x = THEMES.dark;
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'dark-theme');
- });
-});
-
-test('resolveMemberExpression should resolve computed property access', async () => {
- await runTest({}, async () => {
- const code = `
- const THEMES = { dark: "dark-theme", light: "light-theme" };
- const x = THEMES["dark"];
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n) && n.computed);
- assert(found);
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'dark-theme');
- });
-});
+test('resolveMemberExpression should handle valid and invalid member expressions', async () => {
+ const cases = [
+ {
+ description: 'simple object property',
+ code: `const THEMES = { dark: "dark-theme", light: "light-theme" }; const x = THEMES.dark;`,
+ expected: 'dark-theme',
+ },
+ {
+ description: 'computed property access',
+ code: `const THEMES = { dark: "dark-theme", light: "light-theme" }; const x = THEMES["dark"];`,
+ expected: 'dark-theme',
+ },
+ {
+ description: 'nested object property',
+ code: `const THEMES = { brand: { primary: "blue", secondary: "green" } }; const x = THEMES.brand.primary;`,
+ expected: 'blue',
+ },
+ { description: 'array access by index', code: `const COLORS = ["red", "green", "blue"]; const x = COLORS[1];`, expected: 'green' },
+ { description: 'optional member expression', code: `const THEMES = { dark: "dark-theme" }; const x = THEMES?.dark;`, expected: 'dark-theme' },
+ {
+ description: 'computed property using const identifier',
+ code: `const KEY = "dark"; const THEMES = { dark: "dark-theme" }; const x = THEMES[KEY];`,
+ expected: 'dark-theme',
+ },
+ { description: 'TypeScript const assertion', code: `const THEMES = { dark: "dark-theme" } as const; const x = THEMES.dark;`, expected: 'dark-theme' },
+ {
+ description: 'nonexistent property should throw',
+ code: `const THEMES = { dark: "dark-theme" }; const x = THEMES.nonexistent;`,
+ shouldThrow: /cannot be resolved at build-time/,
+ },
+ { description: 'imported object should throw', code: `import { THEMES } from './constants'; const x = THEMES.dark;`, shouldThrow: /Imports Not Allowed/ },
+ ];
-test('resolveMemberExpression should resolve nested object properties', async () => {
await runTest({}, async () => {
- const code = `
- const THEMES = {
- brand: {
- primary: "blue",
- secondary: "green"
- }
- };
- const x = THEMES.brand.primary;
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n) && t.isMemberExpression(n.object));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'blue');
+ for (const testCase of cases) {
+ const { description, code, expected, shouldThrow } = testCase;
+
+ const found = findExpression(code, (n) => t.isMemberExpression(n) || t.isOptionalMemberExpression(n));
+ assert(found, `Expected member expression for: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+
+ if (shouldThrow) {
+ assert.throws(
+ () => {
+ resolveMemberExpression(found.node as any, found.path, literalFromNode, opts);
+ },
+ shouldThrow,
+ `Expected error for: ${description}`
+ );
+ } else {
+ const result = resolveMemberExpression(found.node as any, found.path, literalFromNode, opts);
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
+ }
});
});
-test('resolveMemberExpression should resolve array access', async () => {
- await runTest({}, async () => {
- const code = `
- const COLORS = ["red", "green", "blue"];
- const x = COLORS[1];
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n) && n.computed);
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'green');
- });
-});
+test('literalFromNode should resolve NumericLiterals bound to const identifiers', async () => {
+ const cases = [
+ { description: 'const binding to numeric literal', code: `const IDX = 2; const x = IDX;`, expected: '2' },
+ { description: 'numeric const as object key', code: `const idx = 1; const M = { "1": "yes", 2: "no" }; const x = M[idx];`, expected: 'yes' },
+ {
+ description: 'rejected let-bound numeric literal',
+ code: `let IDX = 0; const LIST = ["a", "b"]; const x = LIST[IDX];`,
+ shouldThrow: /Only top-level `const` variables are allowed/,
+ },
+ ];
-test('resolveMemberExpression should throw for non-existent properties', async () => {
await runTest({}, async () => {
- const code = `
- const THEMES = { dark: "dark-theme" };
- const x = THEMES.nonexistent;
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- assert.throws(() => {
- resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- }, /cannot be resolved at build-time/);
+ for (const testCase of cases) {
+ const { code, expected, shouldThrow, description } = testCase;
+
+ const found = findExpression(code, (n) => t.isIdentifier(n) || t.isMemberExpression(n) || t.isTemplateLiteral(n));
+ assert(found, `Expected expression for: ${description}`);
+
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+
+ if (shouldThrow) {
+ assert.throws(
+ () => {
+ literalFromNode(found.node as t.Expression, found.path, opts);
+ },
+ shouldThrow,
+ `Expected failure: ${description}`
+ );
+ } else {
+ const result = literalFromNode(found.node as t.Expression, found.path, opts);
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
+ }
});
});
-test('resolveMemberExpression should throw for imported objects', async () => {
- await runTest({}, async () => {
- const code = `
- import { THEMES } from './constants';
- const x = THEMES.dark;
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- assert.throws(() => {
- resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- }, /Imports Not Allowed/);
- });
-});
+test('literalFromNode should resolve NumericLiterals bound to const identifiers', async () => {
+ const cases = [
+ {
+ description: 'object access with boolean identifier',
+ code: `const T = "true"; const M = { "true": "yes", "false": "no" }; const x = M[T];`,
+ expected: 'yes',
+ },
+ // { description: 'boolean literal as key', code: `const x = { true: 'yes' }[true];`, expected: 'yes' },
+ { description: 'boolean const used as key', code: `const FLAG = "false"; const x = { true: 'yes', false: 'no' }[FLAG];`, expected: 'no' },
+ ];
-test('resolveMemberExpression should handle optional member expressions', async () => {
await runTest({}, async () => {
- const code = `
- const THEMES = { dark: "dark-theme" };
- const x = THEMES?.dark;
- `;
- const found = findExpression(code, (n) => t.isOptionalMemberExpression(n));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- // Cast to MemberExpression since the function handles both
- const result = resolveMemberExpression(found.node as any, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'dark-theme');
+ for (const testCase of cases) {
+ const { code, expected, description } = testCase;
+ const found = findExpression(code, (n) => t.isIdentifier(n) || t.isMemberExpression(n) || t.isTemplateLiteral(n));
+ assert(found, `Expected expression for: ${description}`);
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+
+ const result = literalFromNode(found.node as t.Expression, found.path, opts);
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
});
});
-test('resolveMemberExpression should handle computed property with const variable', async () => {
- await runTest({}, async () => {
- const code = `
- const KEY = "dark";
- const THEMES = { dark: "dark-theme" };
- const x = THEMES[KEY];
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n) && n.computed);
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'dark-theme');
- });
-});
+test('literalFromNode should resolve SequenceExpression values', async () => {
+ const cases = [
+ { description: 'sequence returns last string literal', code: `const doSomething = () => {}; const x = (doSomething(), "hello");`, expected: 'hello' },
+ { description: 'sequence returns last numeric literal', code: `const doSomething = () => {}; const x = (doSomething(), "42");`, expected: '42' },
+ ];
-test('resolveMemberExpression should handle TS as const assertions', async () => {
await runTest({}, async () => {
- const code = `
- const THEMES = { dark: "dark-theme" } as const;
- const x = THEMES.dark;
- `;
- const found = findExpression(code, (n) => t.isMemberExpression(n));
- assert(found);
-
- const opts: ResolveOpts = { throwOnFail: true, source: code };
- const result = resolveMemberExpression(found.node as t.MemberExpression, found.path, literalFromNode, opts);
- assert.strictEqual(result, 'dark-theme');
+ for (const testCase of cases) {
+ const { code, expected, description } = testCase;
+ const found = findExpression(code, (n) => t.isSequenceExpression(n));
+ assert(found, `Expected expression for: ${description}`);
+ const opts: ResolveOpts = { throwOnFail: true, source: code };
+
+ const result = literalFromNode(found.node as t.Expression, found.path, opts);
+ assert.strictEqual(result, expected, `Failed: ${description}`);
+ }
});
});
diff --git a/packages/core/src/postcss/resolvers.ts b/packages/core/src/postcss/resolvers.ts
index 0c61854..c164b89 100644
--- a/packages/core/src/postcss/resolvers.ts
+++ b/packages/core/src/postcss/resolvers.ts
@@ -10,37 +10,93 @@ export interface ResolveOpts {
}
/**
- * This function will decide which function to call based on the node type
- * 1. String literal
- * 2. Template literal with no expressions
- * 3. Binary expression (a + b)
- * 4. Logical expression (a || b, a ?? b)
- * 5. Identifier bound to local const
- * 6. Template literal with expressions
- * 7. Member expression
- * 8. Optional member expression
- * 9. Everything else is illegal
+ * Higher up the call tree we verify with typescript that the node resolves to a string literal. and has no whitespace.
+ * We just have to resolve it at build time.
+ *
+ * This function will decide which function to call based on the node type (ALL RESOLVE TO STRINGS)
+ * StringLiteral
+ * TemplateLiteral (both static and dynamic expressions resolved recursively)
+ * Identifier (local const)
+ * BinaryExpression (+ operator)
+ * UnaryExpression (covers numeric/string coercions and logical negation explicitly)
+ * LogicalExpression (||, ??)
+ * ConditionalExpression (condition ? expr1 : expr2)
+ * ArrayExpression (["a", "b"][index])
+ * NumericLiteral (coerced)
+ * MemberExpression (static, computed, nested objects/arrays)
+ * OptionalMemberExpression (a?.b)
+ * ObjectExpression (via MemberExpression chains)
+ * BooleanLiteral (handled explicitly by isBooleanLiteral)
+ SequenceExpression (already handled explicitly by taking last expression)
+
* @param node - The node to convert
* @param path - The path to the node
- * @returns The string literal or null if the node is not a string literal or template literal with no expressions or identifier bound to local const
+ * @returns - The string literal resolved or null
*/
export function literalFromNode(node: t.Expression, path: NodePath, opts: ResolveOpts): string | null {
+ // StringLiteral
+ if (t.isStringLiteral(node)) return node.value;
+ // NumericLiteral - convert numbers to strings
+ if (t.isNumericLiteral(node)) return String(node.value);
+ // BooleanLiteral returned as string
+ if (t.isBooleanLiteral(node)) return String(node.value); // → 'true' or 'false'
+ // TemplateLiteral without ${}
+ if (t.isTemplateLiteral(node) && node.expressions.length === 0) return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
+
/* ── Fast path via Babel constant-folder ───────────── */
const ev = fastEval(node, path);
- if (ev.confident && typeof ev.value === 'string') return ev.value;
- // String / template (no ${})
- if (t.isStringLiteral(node)) return node.value;
- if (t.isTemplateLiteral(node) && node.expressions.length === 0) {
- const text = node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
- return text;
+ if (ev.confident && typeof ev.value === 'string') {
+ containsIllegalIdentifiers(node, path, opts); // 👈 throws if invalid
+ return ev.value;
+ }
+
+ // ConditionalExpression
+ if (t.isConditionalExpression(node)) {
+ const testResult = fastEval(node.test, path);
+ if (testResult.confident) {
+ const branch = testResult.value ? node.consequent : node.alternate;
+ return literalFromNode(branch as t.Expression, path, opts);
+ }
+ // If test isn't statically evaluable, return null
+ return null;
}
+
+ // BinaryExpression with + operator
if (t.isBinaryExpression(node) && node.operator === '+') {
+ // Resolve left
const left = literalFromNode(node.left as t.Expression, path, opts);
+ // Resolve right
const right = literalFromNode(node.right as t.Expression, path, opts);
return left !== null && right !== null ? left + right : null;
}
+ // SequenceExpression (already handled explicitly by taking last expression)
+ if (t.isSequenceExpression(node)) {
+ const last = node.expressions.at(-1);
+ if (last) return literalFromNode(last, path, opts);
+ }
+
+ if (t.isUnaryExpression(node)) {
+ const arg = literalFromNode(node.argument as t.Expression, path, opts);
+ if (arg === null) return null;
+
+ switch (node.operator) {
+ case 'typeof':
+ return typeof arg;
+ case '+':
+ return typeof arg === 'number' || !isNaN(Number(arg)) ? String(+arg) : null;
+ case '-':
+ return typeof arg === 'number' || !isNaN(Number(arg)) ? String(-arg) : null;
+ case '!':
+ return String(!arg);
+ case 'void':
+ return 'undefined';
+ default:
+ return null;
+ }
+ }
+
/* ── Logical fallback (a || b , a ?? b) ───────────── */
if (t.isLogicalExpression(node) && (node.operator === '||' || node.operator === '??')) {
// try left; if it resolves, use it, otherwise fall back to right
@@ -49,10 +105,10 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
return literalFromNode(node.right as t.Expression, path, opts);
}
- // Identifier bound to local const (also handles object/array literals
- // via the recursive call inside resolveLocalConstIdentifier)
+ // "Is this node an Identifier?"
+ // "If yes, can I resolve it to a literal value like a string, number, or boolean?"
const idLit = resolveLocalConstIdentifier(node, path, opts);
- if (idLit !== null) return idLit;
+ if (idLit !== null) return String(idLit);
// Template literal with ${expr} or ${CONSTANT}
if (t.isTemplateLiteral(node)) {
@@ -74,17 +130,17 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
Returns {confident, value} or {confident: false}
\*──────────────────────────────────────────────────────────*/
-export function fastEval(node: t.Expression, path: NodePath) {
+export function fastEval(node: t.Expression, path: NodePath): { confident: boolean; value?: string } {
// ❶ If the node *is* the current visitor path, we can evaluate directly.
- if (node === path.node && (path as any).evaluate) {
- return (path as any).evaluate(); // safe, returns {confident, value}
+ if (node === path.node && (path as NodePath).evaluate) {
+ return path.evaluate(); // safe, returns {confident, value}
}
// ❷ Otherwise try to locate a child-path that wraps `node`.
// (Babel exposes .get() only for *named* keys, so we must scan.)
for (const key of Object.keys(path.node)) {
- const sub = (path as any).get?.(key);
- if (sub?.node === node && sub.evaluate) {
+ const sub = (path as NodePath).get?.(key) as NodePath | undefined;
+ if (sub?.node === node && sub?.evaluate) {
return sub.evaluate();
}
}
@@ -100,7 +156,7 @@ export function fastEval(node: t.Expression, path: NodePath) {
1. It is bound in the **same file** (Program scope),
2. Declared with **`const`** (not `let` / `var`),
- 3. Initialised to a **string literal** or a **static template literal**,
+ 3. Initialized to a **string literal** or a **static template literal**,
4. The final string has **no whitespace** (`/^\S+$/`).
Anything else (inner-scope `const`, dynamic value, imported binding, spaces)
@@ -109,11 +165,7 @@ export function fastEval(node: t.Expression, path: NodePath) {
If the binding is *imported*, we delegate to `throwCodeFrame()` so the
developer gets a consistent, actionable error message.
\*──────────────────────────────────────────────────────────*/
-export function resolveLocalConstIdentifier(
- node: t.Expression, // <- widened
- path: NodePath,
- opts: ResolveOpts
-): string | null {
+export function resolveLocalConstIdentifier(node: t.Expression, path: NodePath, opts: ResolveOpts): string | number | boolean | null {
/* Fast-exit when node isn't an Identifier */
if (!t.isIdentifier(node)) return null;
@@ -143,6 +195,14 @@ export function resolveLocalConstIdentifier(
/* 2. Allow only top-level `const` */
if (!binding.path.isVariableDeclarator() || binding.scope.block.type !== 'Program' || (binding.path.parent as t.VariableDeclaration).kind !== 'const') {
+ if ((binding.path.parent as t.VariableDeclaration).kind !== 'const') {
+ throwCodeFrame(
+ path,
+ path.opts?.filename,
+ opts.source ?? path.opts?.source?.code,
+ `[Zero-UI] Only top-level \`const\` variables are allowed. '${node.name}' is not valid.`
+ );
+ }
return null;
}
@@ -155,12 +215,17 @@ export function resolveLocalConstIdentifier(
init = (init as any).expression; // step into the real value
}
- let text: string | null = null;
+ let text: string | number | boolean | null = null;
if (t.isStringLiteral(init)) {
- text = init.value;
+ text = init?.value;
} else if (t.isTemplateLiteral(init)) {
text = resolveTemplateLiteral(init, binding.path, literalFromNode, opts);
+ } else if (t.isNumericLiteral(init)) {
+ const raw = String(init.value);
+ if (/^\S+$/.test(raw)) text = raw;
+ } else if (t.isBooleanLiteral(init)) {
+ text = init.value; // → 'true' or 'false'
}
return text;
@@ -267,15 +332,15 @@ export function resolveMemberExpression(
} else if (t.isNumericLiteral(expr)) {
props.unshift(expr.value);
} else {
- const lit = literalFromNode(expr, path, opts);
- if (lit === null)
+ const lit = literalFromNode(expr, path, { ...opts, throwOnFail: true });
+ if (lit === null) {
throwCodeFrame(
path,
path.opts?.filename,
opts.source ?? path.opts?.source?.code,
- '[Zero-UI] Member expression must resolve to a static space-free string.'
+ '[Zero-UI] Member expression must resolve to a static space-free string.\n' + 'only use const identifiers as keys.'
);
-
+ }
const num = Number(lit);
props.unshift(Number.isFinite(num) ? num : lit);
}
@@ -358,12 +423,60 @@ export function resolveMemberExpression(
\*──────────────────────────────────────────────────────────*/
function resolveObjectValue(obj: t.ObjectExpression, key: string): t.Expression | null | undefined {
for (const p of obj.properties) {
+ // Matches: { dark: 'theme' } — key = 'dark'
if (t.isObjectProperty(p) && !p.computed && t.isIdentifier(p.key) && p.key.name === key) {
return p.value as t.Expression;
}
+
+ // Matches: { ['dark']: 'theme' } — key = 'dark'
if (t.isObjectProperty(p) && p.computed && t.isStringLiteral(p.key) && p.key.value === key) {
return p.value as t.Expression;
}
+
+ // Matches: { "dark": "theme" } or { "1": "theme" } — key = 'dark' or '1'
+ // if (t.isObjectProperty(p) && t.isStringLiteral(p.key) && p.key.value === key) {
+ // return p.value as t.Expression;
+ // }
+
+ // // ✅ New: Matches { 1: "theme" } — key = '1'
+ // if (t.isObjectProperty(p) && t.isNumericLiteral(p.key) && String(p.key.value) === key) {
+ // return p.value as t.Expression;
+ // }
}
+
return null;
}
+
+function containsIllegalIdentifiers(node: t.Node, path: NodePath, opts: ResolveOpts): void {
+ t.traverseFast(node, (subNode) => {
+ if (!t.isIdentifier(subNode)) return;
+
+ const binding = path.scope.getBinding(subNode.name);
+ if (!binding) {
+ throwCodeFrame(
+ path,
+ path.opts?.filename,
+ opts.source ?? path.opts?.source?.code,
+ `[Zero-UI] Identifier '${subNode.name}' is not declared. Only local top-level consts are allowed.`
+ );
+ }
+
+ if (binding.path.isImportSpecifier() || binding.path.isImportDefaultSpecifier() || binding.path.isImportNamespaceSpecifier()) {
+ throwCodeFrame(
+ path,
+ path.opts?.filename,
+ opts.source ?? path.opts?.source?.code,
+ `[Zero-UI] Imports Not Allowed:\n Inline it or alias to a local const first.`
+ );
+ }
+
+ if (binding.scope.block.type !== 'Program' || (binding.path.parent as t.VariableDeclaration).kind !== 'const') {
+ throwCodeFrame(
+ path,
+ path.opts?.filename,
+ opts.source ?? path.opts?.source?.code,
+ `[Zero-UI] Only top-level \`const\` variables are allowed. '${subNode.name}' is not valid.`
+ );
+ }
+ });
+}
diff --git a/packages/core/src/postcss/utilities.ts b/packages/core/src/postcss/utilities.ts
index cd9248b..98c8eca 100644
--- a/packages/core/src/postcss/utilities.ts
+++ b/packages/core/src/postcss/utilities.ts
@@ -1,5 +1,3 @@
-import { Result } from 'postcss';
-
export function formatError(err: unknown) {
const error = err instanceof Error ? err : new Error(String(err));
const eWithLoc = error as Error & { loc?: { file?: string; line?: number; column?: number } };
@@ -24,6 +22,13 @@ export function formatError(err: unknown) {
return { friendly, loc: eWithLoc.loc };
}
+export type Result = {
+ messages: { type: string; plugin: string; file: string; parent: string }[];
+ opts: { from: string };
+ prepend: (css: string) => void;
+ warn: (message: string, options?: { endIndex?: number; index?: number; node?: Node; plugin?: string; word?: string }) => void;
+};
+
export function registerDeps(result: Result, plugin: string, files: string[], parent: string) {
files.forEach((file) => {
result.messages.push({ type: 'dependency', plugin, file, parent });
diff --git a/packages/core/src/postcss/vite.ts b/packages/core/src/postcss/vite.ts
index 26280f0..a87b06a 100644
--- a/packages/core/src/postcss/vite.ts
+++ b/packages/core/src/postcss/vite.ts
@@ -1,18 +1,18 @@
//src/postcss/vite.ts
// vite-plugin-zero-ui.ts (ESM wrapper)
-import tailwindcss from '@tailwindcss/postcss';
import zeroUiPostcss from './index.cjs';
+import tailwindcssInternal from '@tailwindcss/postcss';
import path from 'path';
// @ts-ignore
import type { Plugin } from 'vite';
-export default function zeroUI(): Plugin {
+export default function zeroUI({ tailwind }: { tailwind?: () => any } = {}): Plugin {
return {
name: 'vite-react-zero-ui',
enforce: 'pre' as const, // run before other Vite plugins
async config() {
- return { css: { postcss: { plugins: [zeroUiPostcss, tailwindcss()] } } };
+ return { css: { postcss: { plugins: [zeroUiPostcss, tailwind ? tailwind : tailwindcssInternal()] } } };
},
async transformIndexHtml(html: string): Promise {
const { bodyAttributes } = await import(path.join(process.cwd(), './.zero-ui/attributes.js'));
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 10c2fa5..cf08479 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,13 +13,7 @@ importers:
version: 9.32.0
'@types/node':
specifier: ^24.1.0
- version: 24.1.0
- '@typescript-eslint/eslint-plugin':
- specifier: ^8.38.0
- version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
- '@typescript-eslint/parser':
- specifier: ^8.38.0
- version: 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
+ version: 24.2.0
esbuild:
specifier: ^0.25.8
version: 0.25.8
@@ -28,7 +22,7 @@ importers:
version: 9.32.0(jiti@2.5.1)
eslint-plugin-import:
specifier: ^2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1))
+ version: 2.32.0(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-n:
specifier: ^17.21.3
version: 17.21.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
@@ -97,17 +91,8 @@ importers:
packages/cli:
dependencies:
'@react-zero-ui/core':
- specifier: ^0.2.2
- version: 0.2.7(@tailwindcss/postcss@4.1.11)(postcss@8.5.6)(react@19.1.1)(tailwindcss@4.1.11)
- '@tailwindcss/postcss':
- specifier: ^4.1.8
- version: 4.1.11
- postcss:
- specifier: ^8.4.27
- version: 8.5.6
- tailwindcss:
- specifier: ^4.0.0
- version: 4.1.11
+ specifier: ^0.3.1
+ version: 0.3.1(@tailwindcss/postcss@4.1.11)(postcss@8.5.6)(react@19.1.1)(tailwindcss@4.1.11)
packages/core:
dependencies:
@@ -135,15 +120,9 @@ importers:
lru-cache:
specifier: ^11.1.0
version: 11.1.0
- postcss:
- specifier: ^8.5.5
- version: 8.5.6
react:
specifier: '>=16.8.0'
version: 19.1.1
- tailwindcss:
- specifier: ^4.1.10
- version: 4.1.11
devDependencies:
'@playwright/test':
specifier: ^1.54.0
@@ -167,18 +146,18 @@ importers:
packages/eslint-plugin-react-zero-ui:
dependencies:
'@typescript-eslint/parser':
- specifier: ^6.21.0
- version: 6.21.0(eslint@7.32.0)(typescript@5.9.2)
+ specifier: ^8.39.0
+ version: 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
eslint:
- specifier: ^7.7.0
- version: 7.32.0
- devDependencies:
- '@typescript-eslint/utils':
- specifier: ^8.38.0
- version: 8.38.0(eslint@7.32.0)(typescript@5.9.2)
+ specifier: ^9.0.0
+ version: 9.32.0(jiti@2.5.1)
typescript:
- specifier: ^5.4.3
+ specifier: ^5.9.2
version: 5.9.2
+ devDependencies:
+ '@typescript-eslint/utils':
+ specifier: ^8.39.0
+ version: 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
packages:
@@ -190,9 +169,6 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
- '@babel/code-frame@7.12.11':
- resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==}
-
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
@@ -243,10 +219,6 @@ packages:
resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==}
engines: {node: '>=6.9.0'}
- '@babel/highlight@7.25.9':
- resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
- engines: {node: '>=6.9.0'}
-
'@babel/parser@7.28.0':
resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
engines: {node: '>=6.0.0'}
@@ -454,10 +426,6 @@ packages:
resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/eslintrc@0.4.3':
- resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==}
- engines: {node: ^10.12.0 || >=12.0.0}
-
'@eslint/eslintrc@3.3.1':
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -486,19 +454,10 @@ packages:
resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
engines: {node: '>=18.18.0'}
- '@humanwhocodes/config-array@0.5.0':
- resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==}
- engines: {node: '>=10.10.0'}
- deprecated: Use @eslint/config-array instead
-
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
- '@humanwhocodes/object-schema@1.2.1':
- resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
- deprecated: Use @eslint/object-schema instead
-
'@humanwhocodes/retry@0.3.1':
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
engines: {node: '>=18.18'}
@@ -804,8 +763,8 @@ packages:
react: '>=16.8.0'
tailwindcss: ^4.1.10
- '@react-zero-ui/core@0.2.7':
- resolution: {integrity: sha512-LMu+Zm4VHiJOiLNbs5fhLUK0+RxpnlurjgA7D24LuXBDLuYd22cQKiYBUOidi92nmo9uq4s8hMz7TGiAqhcc3w==}
+ '@react-zero-ui/core@0.3.1':
+ resolution: {integrity: sha512-h3A3GOxFyiJ62Ot6g5hAvfF+kEtSimzcCfW41147P2+HUzgdWsYI5ZjadIEwhXF2inBU7eFJcmnm3gSVdfbiOg==}
engines: {node: '>=18.0.0'}
peerDependencies:
'@tailwindcss/postcss': ^4.1.10
@@ -1040,8 +999,8 @@ packages:
'@types/node@20.19.9':
resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==}
- '@types/node@24.1.0':
- resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==}
+ '@types/node@24.2.0':
+ resolution: {integrity: sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==}
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -1071,94 +1030,48 @@ packages:
'@types/yargs@16.0.9':
resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==}
- '@typescript-eslint/eslint-plugin@8.38.0':
- resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- '@typescript-eslint/parser': ^8.38.0
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/parser@6.21.0':
- resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
- engines: {node: ^16.0.0 || >=18.0.0}
- peerDependencies:
- eslint: ^7.0.0 || ^8.0.0
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- '@typescript-eslint/parser@8.38.0':
- resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
+ '@typescript-eslint/parser@8.39.0':
+ resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/project-service@8.38.0':
- resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
+ '@typescript-eslint/project-service@8.39.0':
+ resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/scope-manager@6.21.0':
- resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==}
- engines: {node: ^16.0.0 || >=18.0.0}
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.38.0':
- resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
+ '@typescript-eslint/scope-manager@8.39.0':
+ resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.38.0':
- resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
+ '@typescript-eslint/tsconfig-utils@8.39.0':
+ resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/type-utils@8.38.0':
- resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
+ '@typescript-eslint/types@8.39.0':
+ resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
-
- '@typescript-eslint/types@6.21.0':
- resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==}
- engines: {node: ^16.0.0 || >=18.0.0}
- '@typescript-eslint/types@8.38.0':
- resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
+ '@typescript-eslint/typescript-estree@8.39.0':
+ resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/typescript-estree@6.21.0':
- resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==}
- engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- '@typescript-eslint/typescript-estree@8.38.0':
- resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.38.0':
- resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
+ '@typescript-eslint/utils@8.39.0':
+ resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@6.21.0':
- resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
- engines: {node: ^16.0.0 || >=18.0.0}
-
- '@typescript-eslint/visitor-keys@8.38.0':
- resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
+ '@typescript-eslint/visitor-keys@8.39.0':
+ resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vercel/analytics@1.5.0':
@@ -1196,11 +1109,6 @@ packages:
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn@7.4.1:
- resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
@@ -1213,28 +1121,14 @@ packages:
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
- ajv@8.17.1:
- resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
-
- ansi-colors@4.1.3:
- resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
- engines: {node: '>=6'}
-
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
- ansi-styles@3.2.1:
- resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
- engines: {node: '>=4'}
-
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
- argparse@1.0.10:
- resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
-
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -1249,10 +1143,6 @@ packages:
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
engines: {node: '>= 0.4'}
- array-union@2.1.0:
- resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
- engines: {node: '>=8'}
-
array.prototype.findlastindex@1.2.6:
resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
engines: {node: '>= 0.4'}
@@ -1273,10 +1163,6 @@ packages:
resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
engines: {node: '>=0.10.0'}
- astral-regex@2.0.0:
- resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
- engines: {node: '>=8'}
-
async-function@1.0.0:
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
engines: {node: '>= 0.4'}
@@ -1344,10 +1230,6 @@ packages:
caniuse-lite@1.0.30001731:
resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==}
- chalk@2.4.2:
- resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
- engines: {node: '>=4'}
-
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -1375,16 +1257,10 @@ packages:
engines: {node: '>=18.0.0'}
hasBin: true
- color-convert@1.9.3:
- resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
- color-name@1.1.3:
- resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
@@ -1501,18 +1377,10 @@ packages:
resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==}
engines: {node: '>=0.3.1'}
- dir-glob@3.0.1:
- resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
- engines: {node: '>=8'}
-
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
- doctrine@3.0.0:
- resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
- engines: {node: '>=6.0.0'}
-
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
@@ -1534,8 +1402,8 @@ packages:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
- electron-to-chromium@1.5.194:
- resolution: {integrity: sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==}
+ electron-to-chromium@1.5.195:
+ resolution: {integrity: sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1544,10 +1412,6 @@ packages:
resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
engines: {node: '>=10.13.0'}
- enquirer@2.4.1:
- resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
- engines: {node: '>=8.6'}
-
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -1652,26 +1516,10 @@ packages:
peerDependencies:
eslint: '>=8.23.0'
- eslint-scope@5.1.1:
- resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
- engines: {node: '>=8.0.0'}
-
eslint-scope@8.4.0:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint-utils@2.1.0:
- resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==}
- engines: {node: '>=6'}
-
- eslint-visitor-keys@1.3.0:
- resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
- engines: {node: '>=4'}
-
- eslint-visitor-keys@2.1.0:
- resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
- engines: {node: '>=10'}
-
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1680,12 +1528,6 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@7.32.0:
- resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==}
- engines: {node: ^10.12.0 || >=12.0.0}
- deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
- hasBin: true
-
eslint@9.32.0:
resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1700,15 +1542,6 @@ packages:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- espree@7.3.1:
- resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==}
- engines: {node: ^10.12.0 || >=12.0.0}
-
- esprima@4.0.1:
- resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
- engines: {node: '>=4'}
- hasBin: true
-
esquery@1.6.0:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
@@ -1717,10 +1550,6 @@ packages:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
- estraverse@4.3.0:
- resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
- engines: {node: '>=4.0'}
-
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
@@ -1748,9 +1577,6 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
- fast-uri@3.0.6:
- resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
-
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@@ -1758,10 +1584,6 @@ packages:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
- file-entry-cache@6.0.1:
- resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
- engines: {node: ^10.12.0 || >=12.0.0}
-
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -1778,10 +1600,6 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
- flat-cache@3.2.0:
- resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
- engines: {node: ^10.12.0 || >=12.0.0}
-
flat-cache@4.0.1:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
@@ -1827,9 +1645,6 @@ packages:
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'}
- functional-red-black-tree@1.0.1:
- resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
-
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
@@ -1868,10 +1683,6 @@ packages:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
- globals@13.24.0:
- resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
- engines: {node: '>=8'}
-
globals@14.0.0:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
@@ -1884,10 +1695,6 @@ packages:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
- globby@11.1.0:
- resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
- engines: {node: '>=10'}
-
globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
@@ -1898,9 +1705,6 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- graphemer@1.4.0:
- resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
-
handlebars@4.7.8:
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
engines: {node: '>=0.4.7'}
@@ -1914,10 +1718,6 @@ packages:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
engines: {node: '>= 0.4'}
- has-flag@3.0.0:
- resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
- engines: {node: '>=4'}
-
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -1960,18 +1760,10 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
- ignore@4.0.6:
- resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
- engines: {node: '>= 4'}
-
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
- ignore@7.0.5:
- resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
- engines: {node: '>= 4'}
-
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -2126,10 +1918,6 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
- js-yaml@3.14.1:
- resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
- hasBin: true
-
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
@@ -2152,9 +1940,6 @@ packages:
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
- json-schema-traverse@1.0.0:
- resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -2271,9 +2056,6 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
- lodash.truncate@4.4.2:
- resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
-
lru-cache@11.1.0:
resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
engines: {node: 20 || >=22}
@@ -2323,10 +2105,6 @@ packages:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
- minimatch@9.0.3:
- resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
- engines: {node: '>=16 || 14 >=14.17'}
-
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2514,10 +2292,6 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
- path-type@4.0.0:
- resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
- engines: {node: '>=8'}
-
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -2563,10 +2337,6 @@ packages:
engines: {node: '>=14'}
hasBin: true
- progress@2.0.3:
- resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
- engines: {node: '>=0.4.0'}
-
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2627,10 +2397,6 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
- regexpp@3.2.0:
- resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
- engines: {node: '>=8'}
-
release-please@17.1.1:
resolution: {integrity: sha512-baFGx79P5hGxbtDqf8p+ThwQjHT/byst6EtnCkjfA6FcK5UnaERn6TBI+8wSsaVIohPG2urYjZaPkITxDS3/Pw==}
engines: {node: '>=18.0.0'}
@@ -2640,10 +2406,6 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
- require-from-string@2.0.2:
- resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
- engines: {node: '>=0.10.0'}
-
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2664,11 +2426,6 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
- rimraf@3.0.2:
- resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
- deprecated: Rimraf versions prior to v4 are no longer supported
- hasBin: true
-
rollup@4.46.2:
resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -2751,14 +2508,6 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
- slash@3.0.0:
- resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
- engines: {node: '>=8'}
-
- slice-ansi@4.0.0:
- resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
- engines: {node: '>=10'}
-
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -2782,9 +2531,6 @@ packages:
split@1.0.1:
resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
- sprintf-js@1.0.3:
- resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@@ -2834,10 +2580,6 @@ packages:
babel-plugin-macros:
optional: true
- supports-color@5.5.0:
- resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
- engines: {node: '>=4'}
-
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -2846,10 +2588,6 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- table@6.9.0:
- resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
- engines: {node: '>=10.0.0'}
-
tailwindcss@4.1.11:
resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
@@ -2861,9 +2599,6 @@ packages:
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
engines: {node: '>=18'}
- text-table@0.2.0:
- resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
-
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
@@ -2875,12 +2610,6 @@ packages:
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
engines: {node: '>=8'}
- ts-api-utils@1.4.3:
- resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
- engines: {node: '>=16'}
- peerDependencies:
- typescript: '>=4.2.0'
-
ts-api-utils@2.1.0:
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
engines: {node: '>=18.12'}
@@ -2911,10 +2640,6 @@ packages:
resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
engines: {node: '>=10'}
- type-fest@0.20.2:
- resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
- engines: {node: '>=10'}
-
type-fest@0.6.0:
resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
engines: {node: '>=8'}
@@ -2965,8 +2690,8 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
- undici-types@7.8.0:
- resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
+ undici-types@7.10.0:
+ resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
unist-util-is@4.1.0:
resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==}
@@ -2993,9 +2718,6 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- v8-compile-cache@2.4.0:
- resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==}
-
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
@@ -3055,8 +2777,8 @@ packages:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
- yaml@2.8.0:
- resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
engines: {node: '>= 14.6'}
hasBin: true
@@ -3089,10 +2811,6 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.12
'@jridgewell/trace-mapping': 0.3.29
- '@babel/code-frame@7.12.11':
- dependencies:
- '@babel/highlight': 7.25.9
-
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
@@ -3166,13 +2884,6 @@ snapshots:
'@babel/template': 7.27.2
'@babel/types': 7.28.2
- '@babel/highlight@7.25.9':
- dependencies:
- '@babel/helper-validator-identifier': 7.27.1
- chalk: 2.4.2
- js-tokens: 4.0.0
- picocolors: 1.1.1
-
'@babel/parser@7.28.0':
dependencies:
'@babel/types': 7.28.2
@@ -3299,11 +3010,6 @@ snapshots:
'@esbuild/win32-x64@0.25.8':
optional: true
- '@eslint-community/eslint-utils@4.7.0(eslint@7.32.0)':
- dependencies:
- eslint: 7.32.0
- eslint-visitor-keys: 3.4.3
-
'@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.5.1))':
dependencies:
eslint: 9.32.0(jiti@2.5.1)
@@ -3325,20 +3031,6 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/eslintrc@0.4.3':
- dependencies:
- ajv: 6.12.6
- debug: 4.4.1
- espree: 7.3.1
- globals: 13.24.0
- ignore: 4.0.6
- import-fresh: 3.3.1
- js-yaml: 3.14.1
- minimatch: 3.1.2
- strip-json-comments: 3.1.1
- transitivePeerDependencies:
- - supports-color
-
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
@@ -3375,18 +3067,8 @@ snapshots:
'@humanfs/core': 0.19.1
'@humanwhocodes/retry': 0.3.1
- '@humanwhocodes/config-array@0.5.0':
- dependencies:
- '@humanwhocodes/object-schema': 1.2.1
- debug: 4.4.1
- minimatch: 3.1.2
- transitivePeerDependencies:
- - supports-color
-
'@humanwhocodes/module-importer@1.0.1': {}
- '@humanwhocodes/object-schema@1.2.1': {}
-
'@humanwhocodes/retry@0.3.1': {}
'@humanwhocodes/retry@0.4.3': {}
@@ -3635,7 +3317,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@react-zero-ui/core@0.2.7(@tailwindcss/postcss@4.1.11)(postcss@8.5.6)(react@19.1.1)(tailwindcss@4.1.11)':
+ '@react-zero-ui/core@0.3.1(@tailwindcss/postcss@4.1.11)(postcss@8.5.6)(react@19.1.1)(tailwindcss@4.1.11)':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.0
@@ -3819,9 +3501,9 @@ snapshots:
dependencies:
undici-types: 6.21.0
- '@types/node@24.1.0':
+ '@types/node@24.2.0':
dependencies:
- undici-types: 7.8.0
+ undici-types: 7.10.0
'@types/normalize-package-data@2.4.4': {}
@@ -3847,108 +3529,44 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
- '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
- dependencies:
- '@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
- '@typescript-eslint/scope-manager': 8.38.0
- '@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
- '@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
- '@typescript-eslint/visitor-keys': 8.38.0
- eslint: 9.32.0(jiti@2.5.1)
- graphemer: 1.4.0
- ignore: 7.0.5
- natural-compare: 1.4.0
- ts-api-utils: 2.1.0(typescript@5.9.2)
- typescript: 5.9.2
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/parser@6.21.0(eslint@7.32.0)(typescript@5.9.2)':
- dependencies:
- '@typescript-eslint/scope-manager': 6.21.0
- '@typescript-eslint/types': 6.21.0
- '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.2)
- '@typescript-eslint/visitor-keys': 6.21.0
- debug: 4.4.1
- eslint: 7.32.0
- optionalDependencies:
- typescript: 5.9.2
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
+ '@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/scope-manager': 8.38.0
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
- '@typescript-eslint/visitor-keys': 8.38.0
+ '@typescript-eslint/scope-manager': 8.39.0
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.39.0
debug: 4.4.1
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.38.0(typescript@5.9.2)':
+ '@typescript-eslint/project-service@8.39.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2)
- '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.39.0
debug: 4.4.1
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@6.21.0':
+ '@typescript-eslint/scope-manager@8.39.0':
dependencies:
- '@typescript-eslint/types': 6.21.0
- '@typescript-eslint/visitor-keys': 6.21.0
-
- '@typescript-eslint/scope-manager@8.38.0':
- dependencies:
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/visitor-keys': 8.38.0
-
- '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)':
- dependencies:
- typescript: 5.9.2
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/visitor-keys': 8.39.0
- '@typescript-eslint/type-utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
+ '@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
- '@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
- debug: 4.4.1
- eslint: 9.32.0(jiti@2.5.1)
- ts-api-utils: 2.1.0(typescript@5.9.2)
typescript: 5.9.2
- transitivePeerDependencies:
- - supports-color
- '@typescript-eslint/types@6.21.0': {}
-
- '@typescript-eslint/types@8.38.0': {}
-
- '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.2)':
- dependencies:
- '@typescript-eslint/types': 6.21.0
- '@typescript-eslint/visitor-keys': 6.21.0
- debug: 4.4.1
- globby: 11.1.0
- is-glob: 4.0.3
- minimatch: 9.0.3
- semver: 7.7.2
- ts-api-utils: 1.4.3(typescript@5.9.2)
- optionalDependencies:
- typescript: 5.9.2
- transitivePeerDependencies:
- - supports-color
+ '@typescript-eslint/types@8.39.0': {}
- '@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)':
+ '@typescript-eslint/typescript-estree@8.39.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/project-service': 8.38.0(typescript@5.9.2)
- '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2)
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/visitor-keys': 8.38.0
+ '@typescript-eslint/project-service': 8.39.0(typescript@5.9.2)
+ '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/visitor-keys': 8.39.0
debug: 4.4.1
fast-glob: 3.3.3
is-glob: 4.0.3
@@ -3959,36 +3577,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.38.0(eslint@7.32.0)(typescript@5.9.2)':
- dependencies:
- '@eslint-community/eslint-utils': 4.7.0(eslint@7.32.0)
- '@typescript-eslint/scope-manager': 8.38.0
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
- eslint: 7.32.0
- typescript: 5.9.2
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
+ '@typescript-eslint/utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
- '@typescript-eslint/scope-manager': 8.38.0
- '@typescript-eslint/types': 8.38.0
- '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
+ '@typescript-eslint/scope-manager': 8.39.0
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@6.21.0':
+ '@typescript-eslint/visitor-keys@8.39.0':
dependencies:
- '@typescript-eslint/types': 6.21.0
- eslint-visitor-keys: 3.4.3
-
- '@typescript-eslint/visitor-keys@8.38.0':
- dependencies:
- '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/types': 8.39.0
eslint-visitor-keys: 4.2.1
'@vercel/analytics@1.5.0(next@15.4.5(@babel/core@7.28.0)(@playwright/test@1.54.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)':
@@ -3998,16 +3600,10 @@ snapshots:
'@xmldom/xmldom@0.8.10': {}
- acorn-jsx@5.3.2(acorn@7.4.1):
- dependencies:
- acorn: 7.4.1
-
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
- acorn@7.4.1: {}
-
acorn@8.15.0: {}
agent-base@7.1.4: {}
@@ -4019,29 +3615,12 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
- ajv@8.17.1:
- dependencies:
- fast-deep-equal: 3.1.3
- fast-uri: 3.0.6
- json-schema-traverse: 1.0.0
- require-from-string: 2.0.2
-
- ansi-colors@4.1.3: {}
-
ansi-regex@5.0.1: {}
- ansi-styles@3.2.1:
- dependencies:
- color-convert: 1.9.3
-
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
- argparse@1.0.10:
- dependencies:
- sprintf-js: 1.0.3
-
argparse@2.0.1: {}
array-buffer-byte-length@1.0.2:
@@ -4062,8 +3641,6 @@ snapshots:
is-string: 1.1.1
math-intrinsics: 1.1.0
- array-union@2.1.0: {}
-
array.prototype.findlastindex@1.2.6:
dependencies:
call-bind: 1.0.8
@@ -4100,8 +3677,6 @@ snapshots:
arrify@1.0.1: {}
- astral-regex@2.0.0: {}
-
async-function@1.0.0: {}
async-retry@1.3.3:
@@ -4141,7 +3716,7 @@ snapshots:
browserslist@4.25.1:
dependencies:
caniuse-lite: 1.0.30001731
- electron-to-chromium: 1.5.194
+ electron-to-chromium: 1.5.195
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.1)
@@ -4174,12 +3749,6 @@ snapshots:
caniuse-lite@1.0.30001731: {}
- chalk@2.4.2:
- dependencies:
- ansi-styles: 3.2.1
- escape-string-regexp: 1.0.5
- supports-color: 5.5.0
-
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -4213,16 +3782,10 @@ snapshots:
parse-diff: 0.11.1
yargs: 16.2.0
- color-convert@1.9.3:
- dependencies:
- color-name: 1.1.3
-
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
- color-name@1.1.3: {}
-
color-name@1.1.4: {}
color-string@1.9.1:
@@ -4342,18 +3905,10 @@ snapshots:
diff@7.0.0: {}
- dir-glob@3.0.1:
- dependencies:
- path-type: 4.0.0
-
doctrine@2.1.0:
dependencies:
esutils: 2.0.3
- doctrine@3.0.0:
- dependencies:
- esutils: 2.0.3
-
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
@@ -4382,7 +3937,7 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
- electron-to-chromium@1.5.194: {}
+ electron-to-chromium@1.5.195: {}
emoji-regex@8.0.0: {}
@@ -4391,11 +3946,6 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.2
- enquirer@2.4.1:
- dependencies:
- ansi-colors: 4.1.3
- strip-ansi: 6.0.1
-
entities@4.5.0: {}
error-ex@1.3.2:
@@ -4532,11 +4082,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.5.1)):
+ eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
@@ -4549,7 +4098,7 @@ snapshots:
eslint: 9.32.0(jiti@2.5.1)
eslint-compat-utils: 0.5.1(eslint@9.32.0(jiti@2.5.1))
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)):
+ eslint-plugin-import@2.32.0(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -4560,7 +4109,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.5.1))
+ eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.5.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -4571,8 +4120,6 @@ snapshots:
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
- optionalDependencies:
- '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -4593,73 +4140,15 @@ snapshots:
transitivePeerDependencies:
- typescript
- eslint-scope@5.1.1:
- dependencies:
- esrecurse: 4.3.0
- estraverse: 4.3.0
-
eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
- eslint-utils@2.1.0:
- dependencies:
- eslint-visitor-keys: 1.3.0
-
- eslint-visitor-keys@1.3.0: {}
-
- eslint-visitor-keys@2.1.0: {}
-
eslint-visitor-keys@3.4.3: {}
eslint-visitor-keys@4.2.1: {}
- eslint@7.32.0:
- dependencies:
- '@babel/code-frame': 7.12.11
- '@eslint/eslintrc': 0.4.3
- '@humanwhocodes/config-array': 0.5.0
- ajv: 6.12.6
- chalk: 4.1.2
- cross-spawn: 7.0.6
- debug: 4.4.1
- doctrine: 3.0.0
- enquirer: 2.4.1
- escape-string-regexp: 4.0.0
- eslint-scope: 5.1.1
- eslint-utils: 2.1.0
- eslint-visitor-keys: 2.1.0
- espree: 7.3.1
- esquery: 1.6.0
- esutils: 2.0.3
- fast-deep-equal: 3.1.3
- file-entry-cache: 6.0.1
- functional-red-black-tree: 1.0.1
- glob-parent: 5.1.2
- globals: 13.24.0
- ignore: 4.0.6
- import-fresh: 3.3.1
- imurmurhash: 0.1.4
- is-glob: 4.0.3
- js-yaml: 3.14.1
- json-stable-stringify-without-jsonify: 1.0.1
- levn: 0.4.1
- lodash.merge: 4.6.2
- minimatch: 3.1.2
- natural-compare: 1.4.0
- optionator: 0.9.4
- progress: 2.0.3
- regexpp: 3.2.0
- semver: 7.7.2
- strip-ansi: 6.0.1
- strip-json-comments: 3.1.1
- table: 6.9.0
- text-table: 0.2.0
- v8-compile-cache: 2.4.0
- transitivePeerDependencies:
- - supports-color
-
eslint@9.32.0(jiti@2.5.1):
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
@@ -4708,14 +4197,6 @@ snapshots:
acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 4.2.1
- espree@7.3.1:
- dependencies:
- acorn: 7.4.1
- acorn-jsx: 5.3.2(acorn@7.4.1)
- eslint-visitor-keys: 1.3.0
-
- esprima@4.0.1: {}
-
esquery@1.6.0:
dependencies:
estraverse: 5.3.0
@@ -4724,8 +4205,6 @@ snapshots:
dependencies:
estraverse: 5.3.0
- estraverse@4.3.0: {}
-
estraverse@5.3.0: {}
estree-walker@2.0.2: {}
@@ -4750,8 +4229,6 @@ snapshots:
fast-levenshtein@2.0.6: {}
- fast-uri@3.0.6: {}
-
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -4760,10 +4237,6 @@ snapshots:
dependencies:
escape-string-regexp: 1.0.5
- file-entry-cache@6.0.1:
- dependencies:
- flat-cache: 3.2.0
-
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -4782,12 +4255,6 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
- flat-cache@3.2.0:
- dependencies:
- flatted: 3.3.3
- keyv: 4.5.4
- rimraf: 3.0.2
-
flat-cache@4.0.1:
dependencies:
flatted: 3.3.3
@@ -4827,8 +4294,6 @@ snapshots:
hasown: 2.0.2
is-callable: 1.2.7
- functional-red-black-tree@1.0.1: {}
-
functions-have-names@1.2.3: {}
gensync@1.0.0-beta.2: {}
@@ -4880,10 +4345,6 @@ snapshots:
once: 1.4.0
path-is-absolute: 1.0.1
- globals@13.24.0:
- dependencies:
- type-fest: 0.20.2
-
globals@14.0.0: {}
globals@15.15.0: {}
@@ -4893,23 +4354,12 @@ snapshots:
define-properties: 1.2.1
gopd: 1.2.0
- globby@11.1.0:
- dependencies:
- array-union: 2.1.0
- dir-glob: 3.0.1
- fast-glob: 3.3.3
- ignore: 5.3.2
- merge2: 1.4.1
- slash: 3.0.0
-
globrex@0.1.2: {}
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
- graphemer@1.4.0: {}
-
handlebars@4.7.8:
dependencies:
minimist: 1.2.8
@@ -4923,8 +4373,6 @@ snapshots:
has-bigints@1.1.0: {}
- has-flag@3.0.0: {}
-
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
@@ -4967,12 +4415,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ignore@4.0.6: {}
-
ignore@5.3.2: {}
- ignore@7.0.5: {}
-
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -5121,11 +4565,6 @@ snapshots:
js-tokens@4.0.0: {}
- js-yaml@3.14.1:
- dependencies:
- argparse: 1.0.10
- esprima: 4.0.1
-
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
@@ -5140,8 +4579,6 @@ snapshots:
json-schema-traverse@0.4.1: {}
- json-schema-traverse@1.0.0: {}
-
json-stable-stringify-without-jsonify@1.0.1: {}
json-stringify-safe@5.0.1: {}
@@ -5230,8 +4667,6 @@ snapshots:
lodash.merge@4.6.2: {}
- lodash.truncate@4.4.2: {}
-
lru-cache@11.1.0: {}
lru-cache@5.1.1:
@@ -5283,10 +4718,6 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
- minimatch@9.0.3:
- dependencies:
- brace-expansion: 2.0.2
-
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.2
@@ -5475,8 +4906,6 @@ snapshots:
path-parse@1.0.7: {}
- path-type@4.0.0: {}
-
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -5511,8 +4940,6 @@ snapshots:
prettier@3.6.2: {}
- progress@2.0.3: {}
-
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
@@ -5593,8 +5020,6 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
- regexpp@3.2.0: {}
-
release-please@17.1.1:
dependencies:
'@conventional-commits/parser': 0.4.1
@@ -5626,15 +5051,13 @@ snapshots:
unist-util-visit: 2.0.3
unist-util-visit-parents: 3.1.1
xpath: 0.0.34
- yaml: 2.8.0
+ yaml: 2.8.1
yargs: 17.7.2
transitivePeerDependencies:
- supports-color
require-directory@2.1.1: {}
- require-from-string@2.0.2: {}
-
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -5649,10 +5072,6 @@ snapshots:
reusify@1.1.0: {}
- rimraf@3.0.2:
- dependencies:
- glob: 7.2.3
-
rollup@4.46.2:
dependencies:
'@types/estree': 1.0.8
@@ -5804,14 +5223,6 @@ snapshots:
sisteransi@1.0.5: {}
- slash@3.0.0: {}
-
- slice-ansi@4.0.0:
- dependencies:
- ansi-styles: 4.3.0
- astral-regex: 2.0.0
- is-fullwidth-code-point: 3.0.0
-
source-map-js@1.2.1: {}
source-map@0.6.1: {}
@@ -5834,8 +5245,6 @@ snapshots:
dependencies:
through: 2.3.8
- sprintf-js@1.0.3: {}
-
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -5889,24 +5298,12 @@ snapshots:
optionalDependencies:
'@babel/core': 7.28.0
- supports-color@5.5.0:
- dependencies:
- has-flag: 3.0.0
-
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {}
- table@6.9.0:
- dependencies:
- ajv: 8.17.1
- lodash.truncate: 4.4.2
- slice-ansi: 4.0.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
-
tailwindcss@4.1.11: {}
tapable@2.2.2: {}
@@ -5920,8 +5317,6 @@ snapshots:
mkdirp: 3.0.1
yallist: 5.0.0
- text-table@0.2.0: {}
-
through@2.3.8: {}
to-regex-range@5.0.1:
@@ -5930,10 +5325,6 @@ snapshots:
trim-newlines@3.0.1: {}
- ts-api-utils@1.4.3(typescript@5.9.2):
- dependencies:
- typescript: 5.9.2
-
ts-api-utils@2.1.0(typescript@5.9.2):
dependencies:
typescript: 5.9.2
@@ -5965,8 +5356,6 @@ snapshots:
type-fest@0.18.1: {}
- type-fest@0.20.2: {}
-
type-fest@0.6.0: {}
type-fest@0.8.1: {}
@@ -6022,7 +5411,7 @@ snapshots:
undici-types@6.21.0: {}
- undici-types@7.8.0: {}
+ undici-types@7.10.0: {}
unist-util-is@4.1.0: {}
@@ -6055,8 +5444,6 @@ snapshots:
dependencies:
punycode: 2.3.1
- v8-compile-cache@2.4.0: {}
-
validate-npm-package-license@3.0.4:
dependencies:
spdx-correct: 3.2.0
@@ -6132,7 +5519,7 @@ snapshots:
yallist@5.0.0: {}
- yaml@2.8.0: {}
+ yaml@2.8.1: {}
yargs-parser@20.2.9: {}
From f90237b461e7ff007d1e75b11b831729d7ee2d05 Mon Sep 17 00:00:00 2001
From: Austin1serb
Date: Tue, 5 Aug 2025 18:40:19 -0700
Subject: [PATCH 03/11] added verbose mode
---
docs/internal.md | 21 +-
docs/rules.md | 0
.../__tests__/fixtures/vite/vite.config.ts | 3 +-
packages/core/src/cli/postInstall.ts | 7 -
packages/core/src/postcss/ast-parsing.test.ts | 188 +++++++++---------
packages/core/src/postcss/ast-parsing.ts | 12 +-
packages/core/src/postcss/resolvers.ts | 54 ++++-
.../fixtures/next/app/ChildComponent.tsx | 4 +-
.../__tests__/fixtures/next/app/page.tsx | 8 +-
9 files changed, 163 insertions(+), 134 deletions(-)
create mode 100644 docs/rules.md
diff --git a/docs/internal.md b/docs/internal.md
index 4cd1a62..a8fa913 100644
--- a/docs/internal.md
+++ b/docs/internal.md
@@ -51,14 +51,19 @@ source files ─► ├─► Map>
};
```
-5. **Emit Tailwind `@custom-variant`s** for every `key‑value` pair `(helpers.cts) → buildCss`
-
- ```css
- @custom-variant ${keySlug}-${valSlug} {
- &:where(body[data-${keySlug}="${valSlug}"] *) { @slot; }
- [data-${keySlug}="${valSlug}"] &, &[data-${keySlug}="${valSlug}"] { @slot; }
- }
- ```
+5. **Emit Tailwind** `@custom-variant` for every `key‑value` pair `(helpers.cts) → buildCss`
+
+```ts
+function buildLocalSelector(keySlug: string, valSlug: string): string {
+ return
+ `[data-${keySlug}="${valSlug}"] &, &[data-${keySlug}="${valSlug}"] { @slot; }`;
+}
+
+function buildGlobalSelector(keySlug: string, valSlug: string): string {
+ return
+ `&:where(body[data-${keySlug}='${valSlug}'] &) { @slot; }`;
+}
+```
6. **Generate the attributes file** so SSR can inject the `` data‑attributes `(helpers.cts) → generateAttributesFile`.
diff --git a/docs/rules.md b/docs/rules.md
new file mode 100644
index 0000000..e69de29
diff --git a/packages/core/__tests__/fixtures/vite/vite.config.ts b/packages/core/__tests__/fixtures/vite/vite.config.ts
index 574010f..1d610da 100644
--- a/packages/core/__tests__/fixtures/vite/vite.config.ts
+++ b/packages/core/__tests__/fixtures/vite/vite.config.ts
@@ -1,9 +1,8 @@
import zeroUI from "@react-zero-ui/core/vite";
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
-import tailwindCss from '@tailwindcss/postcss';
// https://vite.dev/config/
export default defineConfig({
- plugins: [zeroUI({tailwind: tailwindCss}), react()]
+ plugins: [zeroUI(), react()]
});
\ No newline at end of file
diff --git a/packages/core/src/cli/postInstall.ts b/packages/core/src/cli/postInstall.ts
index 34cadbe..d8aeef3 100644
--- a/packages/core/src/cli/postInstall.ts
+++ b/packages/core/src/cli/postInstall.ts
@@ -4,13 +4,6 @@ import { generateAttributesFile, patchTsConfig, patchPostcssConfig, patchViteCon
import { processVariants } from '../postcss/ast-parsing.js';
export async function runZeroUiInit() {
- // 0️⃣ Check peer dependency
- try {
- require.resolve('@tailwindcss/postcss');
- } catch {
- console.error('\n[Zero-UI] ❌ Missing peer dependency "@tailwindcss/postcss".\n' + 'Run: npm install @tailwindcss/postcss\n');
- process.exit(1);
- }
try {
console.log('[Zero-UI] Initializing...');
diff --git a/packages/core/src/postcss/ast-parsing.test.ts b/packages/core/src/postcss/ast-parsing.test.ts
index 90f9eef..4fd3902 100644
--- a/packages/core/src/postcss/ast-parsing.test.ts
+++ b/packages/core/src/postcss/ast-parsing.test.ts
@@ -4,123 +4,119 @@ import { collectUseUIHooks, processVariants } from './ast-parsing.js';
import { parse } from '@babel/parser';
import { readFile, runTest } from './test-utilities.js';
-test('collectUseUIHooks should collect setters from a component', async () => {
- await runTest(
- {
- 'app/boolean-edge-cases.tsx': `
- import { useUI } from '@react-zero-ui/core';
+// test('collectUseUIHooks should collect setters from a component', async () => {
+// await runTest(
+// {
+// 'app/boolean-edge-cases.tsx': `
+// import { useUI } from '@react-zero-ui/core';
- const featureEnabled = 'feature-enabled';
- const bool = false;
+// const featureEnabled = 'feature-enabled';
+// const bool = false;
- const theme = ['true'];
+// const theme = ['true'];
-export function Component() {
- const [isVisible, setIsVisible] = useUI<'modal-visible', 'false'>('modal-visible', 'false');
- const [isEnabled, setIsEnabled] = useUI<'feature-enabled', 'true'>(bool ?? featureEnabled, theme[0]);
-return (
-
-); }`,
- },
+// export function Component() {
+// const [isVisible, setIsVisible] = useUI<'modal-visible', 'false'>('modal-visible', 'false');
+// const [isEnabled, setIsEnabled] = useUI<'feature-enabled', 'true'>(bool ?? featureEnabled, theme[0]);
+// return (
+//
+//
+// Open Modal
+//
+//
+// ); }`,
+// },
- async () => {
- const src = readFile('app/boolean-edge-cases.tsx');
- const ast = parse(src, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
- const setters = collectUseUIHooks(ast, src);
- // assert(setters[0].binding !== null, 'binding should not be null');
- assert(setters[0].initialValue === 'false');
- assert(setters[0].stateKey === 'modal-visible');
- assert(setters[0].setterFnName === 'setIsVisible');
- // assert(setters[1].binding !== null, 'binding should not be null');
- assert(setters[1].initialValue === 'true');
- assert(setters[1].stateKey === 'feature-enabled');
- assert(setters[1].setterFnName === 'setIsEnabled');
+// async () => {
+// const src = readFile('app/boolean-edge-cases.tsx');
+// const ast = parse(src, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
+// const setters = collectUseUIHooks(ast, src);
+// // assert(setters[0].binding !== null, 'binding should not be null');
+// assert(setters[0].initialValue === 'false');
+// assert(setters[0].stateKey === 'modal-visible');
+// assert(setters[0].setterFnName === 'setIsVisible');
+// // assert(setters[1].binding !== null, 'binding should not be null');
+// assert(setters[1].initialValue === 'true');
+// assert(setters[1].stateKey === 'feature-enabled');
+// assert(setters[1].setterFnName === 'setIsEnabled');
- const { finalVariants } = await processVariants(['app/boolean-edge-cases.tsx']);
- assert(finalVariants[0].key === 'feature-enabled');
- assert(finalVariants[0].values.includes('false'));
- assert(finalVariants[0].initialValue === 'true');
- assert(finalVariants[1].key === 'modal-visible');
- assert(finalVariants[1].values.includes('true'));
- assert(finalVariants[1].initialValue === 'false', 'initialValue should be false');
- }
- );
-});
+// const { finalVariants } = await processVariants(['app/boolean-edge-cases.tsx']);
+// assert(finalVariants[0].key === 'feature-enabled');
+// assert(finalVariants[0].values.includes('false'));
+// assert(finalVariants[0].initialValue === 'true');
+// assert(finalVariants[1].key === 'modal-visible');
+// assert(finalVariants[1].values.includes('true'));
+// assert(finalVariants[1].initialValue === 'false', 'initialValue should be false');
+// }
+// );
+// });
-test('collectUseUIHooks should resolve const-based args for useScopedUI', async () => {
- await runTest(
- {
- 'app/tabs.tsx': `
- import { useScopedUI } from '@react-zero-ui/core';
+// test('collectUseUIHooks should resolve const-based args for useScopedUI', async () => {
+// await runTest(
+// {
+// 'app/tabs.tsx': `
+// import { useScopedUI } from '@react-zero-ui/core';
- const KEY = 'tabs';
- const DEFAULT = 'first';
+// const KEY = 'tabs';
+// const DEFAULT = 'first';
- export function Tabs() {
- const [, setTab] = useScopedUI<'tabs', 'first'>(KEY, DEFAULT);
- return ;
- }
- `,
- },
- async () => {
- const src = readFile('app/tabs.tsx');
- const ast = parse(src, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
- const [meta] = collectUseUIHooks(ast, src);
+// export function Tabs() {
+// const [, setTab] = useScopedUI<'tabs', 'first'>(KEY, DEFAULT);
+// return ;
+// }
+// `,
+// },
+// async () => {
+// const src = readFile('app/tabs.tsx');
+// const ast = parse(src, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
+// const [meta] = collectUseUIHooks(ast, src);
- assert.equal(meta.stateKey, 'tabs');
- assert.equal(meta.initialValue, 'first');
- assert.equal(meta.scope, 'scoped');
- }
- );
-});
+// assert.equal(meta.stateKey, 'tabs');
+// assert.equal(meta.initialValue, 'first');
+// assert.equal(meta.scope, 'scoped');
+// }
+// );
+// });
-test('collectUseUIHooks handles + both hooks', async () => {
- const code = `
- import { useUI, useScopedUI } from '@react-zero-ui/core';
- export function Comp() {
- const [, setTheme] = useUI<'theme', 'dark'>('theme','dark');
- const [, setAcc] = useScopedUI<'accordion', 'closed'>('accordion','closed');
- return ;
- }
- `;
- const ast = parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
- const hooks = collectUseUIHooks(ast, code);
- console.log('hooks: ', hooks);
+// test('collectUseUIHooks handles + both hooks', async () => {
+// const code = `
+// import { useUI, useScopedUI } from '@react-zero-ui/core';
+// export function Comp() {
+// const [, setTheme] = useUI<'theme', 'dark'>('theme','dark');
+// const [, setAcc] = useScopedUI<'accordion', 'closed'>('accordion','closed');
+// return ;
+// }
+// `;
+// const ast = parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
+// const hooks = collectUseUIHooks(ast, code);
+// console.log('hooks: ', hooks);
- assert.equal(hooks.length, 2);
- assert.deepEqual(
- hooks.map((h) => [h.stateKey, h.scope]),
- [
- ['theme', 'global'],
- ['accordion', 'scoped'],
- ]
- );
-});
+// assert.equal(hooks.length, 2);
+// assert.deepEqual(
+// hooks.map((h) => [h.stateKey, h.scope]),
+// [
+// ['theme', 'global'],
+// ['accordion', 'scoped'],
+// ]
+// );
+// });
test('collectUseUIHooks NumericLiterals bound to const identifiers', async () => {
const code = `
import { useUI, useScopedUI } from '@react-zero-ui/core';
const T = "true"; const M = { "true": "dark", "false": "light" }; const x = M[T]
export function Comp() {
- const [, setTheme] = useUI<'theme', 'dark'>('theme', x);
- const [, setAcc] = useScopedUI<'accordion', 'closed'>('accordion','closed');
+ const [, setThemeM] = useUI<'light' | 'dark'>('theme-m', x);
+ const [, setAcc] = useScopedUI<'accordion'| 'closed'>('accordion','closed');
return ;
}
`;
const ast = parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
const hooks = collectUseUIHooks(ast, code);
+ console.log('hooks: ', hooks);
- assert.equal(hooks.length, 2);
- assert.deepEqual(
- hooks.map((h) => [h.stateKey, h.scope]),
- [
- ['theme', 'global'],
- ['accordion', 'scoped'],
- ]
- );
+ assert.throws(() => {
+ collectUseUIHooks(ast, code);
+ }, /Cannot use imported variables. Assign to a local const first./);
});
diff --git a/packages/core/src/postcss/ast-parsing.ts b/packages/core/src/postcss/ast-parsing.ts
index fcb5f51..acc1f57 100644
--- a/packages/core/src/postcss/ast-parsing.ts
+++ b/packages/core/src/postcss/ast-parsing.ts
@@ -99,22 +99,26 @@ export function collectUseUIHooks(ast: t.File, sourceCode: string): HookMeta[] {
keyArg,
path.opts?.filename,
sourceCode,
- // TODO add link to docs
- `[Zero-UI] State key cannot be resolved at build-time.\n` + `Only local, fully-static strings are supported. - collectUseUIHooks-stateKey`
+
+ `[Zero-UI] State key cannot be resolved at build-time.\n` +
+ `Only local, fully-static strings are supported. - collectUseUIHooks-stateKey` +
+ `\nSee: https://github.com/react-zero-ui/core/blob/main/docs/assets/internal.md`
);
}
// resolve initial value with helpers
const initialValue = resolveLiteralMemoized(initialArg as t.Expression, path as NodePath, 'initialValue');
+ console.log('initialValue: ', initialValue);
if (initialValue === null) {
throwCodeFrame(
initialArg,
path.opts?.filename,
sourceCode,
- // TODO add link to docs
+
`[Zero-UI] initial value cannot be resolved at build-time.\n` +
- `Only local, fully-static objects/arrays are supported. - collectUseUIHooks-initialValue`
+ `Only local, fully-static objects/arrays are supported. - collectUseUIHooks-initialValue` +
+ `\nSee: https://github.com/react-zero-ui/core/blob/main/docs/assets/internal.md`
);
}
diff --git a/packages/core/src/postcss/resolvers.ts b/packages/core/src/postcss/resolvers.ts
index c164b89..55cd394 100644
--- a/packages/core/src/postcss/resolvers.ts
+++ b/packages/core/src/postcss/resolvers.ts
@@ -3,6 +3,8 @@ import * as t from '@babel/types';
import { NodePath } from '@babel/traverse';
import { throwCodeFrame } from './ast-parsing.js';
import { generate } from '@babel/generator';
+
+const VERBOSE = true;
export interface ResolveOpts {
throwOnFail?: boolean; // default false
source?: string; // optional; fall back to path.hub.file.code
@@ -43,6 +45,8 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
// TemplateLiteral without ${}
if (t.isTemplateLiteral(node) && node.expressions.length === 0) return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
+ VERBOSE && console.log('48 -> literalFromNode');
+
/* ── Fast path via Babel constant-folder ───────────── */
const ev = fastEval(node, path);
@@ -51,6 +55,8 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
return ev.value;
}
+ VERBOSE && console.log('58 -> literalFromNode -> ev: ');
+
// ConditionalExpression
if (t.isConditionalExpression(node)) {
const testResult = fastEval(node.test, path);
@@ -62,6 +68,8 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
return null;
}
+ VERBOSE && console.log('70 -> literalFromNode');
+
// BinaryExpression with + operator
if (t.isBinaryExpression(node) && node.operator === '+') {
// Resolve left
@@ -71,12 +79,16 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
return left !== null && right !== null ? left + right : null;
}
+ VERBOSE && console.log('82 -> literalFromNode');
+
// SequenceExpression (already handled explicitly by taking last expression)
if (t.isSequenceExpression(node)) {
const last = node.expressions.at(-1);
if (last) return literalFromNode(last, path, opts);
}
+ VERBOSE && console.log('89 -> literalFromNode');
+
if (t.isUnaryExpression(node)) {
const arg = literalFromNode(node.argument as t.Expression, path, opts);
if (arg === null) return null;
@@ -97,6 +109,8 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
}
}
+ VERBOSE && console.log('112 -> literalFromNode');
+
/* ── Logical fallback (a || b , a ?? b) ───────────── */
if (t.isLogicalExpression(node) && (node.operator === '||' || node.operator === '??')) {
// try left; if it resolves, use it, otherwise fall back to right
@@ -105,21 +119,31 @@ export function literalFromNode(node: t.Expression, path: NodePath, opts
return literalFromNode(node.right as t.Expression, path, opts);
}
+ VERBOSE && console.log('122 -> literalFromNode');
+
// "Is this node an Identifier?"
// "If yes, can I resolve it to a literal value like a string, number, or boolean?"
const idLit = resolveLocalConstIdentifier(node, path, opts);
+ VERBOSE && console.log('127 -> literalFromNode -> idLit: ', idLit);
if (idLit !== null) return String(idLit);
+ VERBOSE && console.log('130 -> literalFromNode');
+
// Template literal with ${expr} or ${CONSTANT}
if (t.isTemplateLiteral(node)) {
+ VERBOSE && console.log('135 -> literalFromNode -> template literal');
return resolveTemplateLiteral(node, path, literalFromNode, opts);
}
+ VERBOSE && console.log('138 -> literalFromNode');
+
if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
// treat optional-member exactly the same
return resolveMemberExpression(node as t.MemberExpression, path, literalFromNode, opts);
}
+ VERBOSE && console.log('END -> literalFromNode', node);
+
return null;
}
@@ -150,14 +174,11 @@ export function fastEval(node: t.Expression, path: NodePath): { confiden
}
/*──────────────────────────────────────────────────────────*\
- resolveLocalConstIdentifier
- ---------------------------
- Resolve an **Identifier** node to a *space-free string literal* **only when**
+ Resolve an **Identifier** node
1. It is bound in the **same file** (Program scope),
2. Declared with **`const`** (not `let` / `var`),
3. Initialized to a **string literal** or a **static template literal**,
- 4. The final string has **no whitespace** (`/^\S+$/`).
Anything else (inner-scope `const`, dynamic value, imported binding, spaces)
➜ return `null` — the caller will decide whether to throw or keep searching.
@@ -166,6 +187,7 @@ export function fastEval(node: t.Expression, path: NodePath): { confiden
developer gets a consistent, actionable error message.
\*──────────────────────────────────────────────────────────*/
export function resolveLocalConstIdentifier(node: t.Expression, path: NodePath, opts: ResolveOpts): string | number | boolean | null {
+ VERBOSE && console.log('resolveLocalConstIdentifier -> 190');
/* Fast-exit when node isn't an Identifier */
if (!t.isIdentifier(node)) return null;
@@ -218,7 +240,7 @@ export function resolveLocalConstIdentifier(node: t.Expression, path: NodePath string | null,
opts: ResolveOpts
): string | null {
+ VERBOSE && console.log('resolveMemberExpression -> 352');
/** Collect the property chain (deep → shallow) */
const props: (string | number)[] = [];
let current: t.Expression | t.PrivateName = node;
@@ -390,7 +410,7 @@ export function resolveMemberExpression(
}
}
- /* ── NEW: bail-out with an explicit error if nothing was found ───────── */
+ /* ── bail-out with an explicit error if nothing was found ───────── */
if (value == null) {
throwCodeFrame(
path,
@@ -401,17 +421,29 @@ export function resolveMemberExpression(
}
/* ─────────────────────────────────────────────────────── */
- /* existing tail logic (unwrap, recurse, return string)… */
+ /* ── existing unwrap ─ */
if (t.isTSAsExpression(value)) value = value.expression;
+ /* ── recursively resolve nested member expressions ─ */
if (t.isMemberExpression(value)) {
return resolveMemberExpression(value, path, literalFromNode, opts);
}
+ /* ── support literals ─ */
if (t.isStringLiteral(value)) return value.value;
if (t.isTemplateLiteral(value)) {
return resolveTemplateLiteral(value, path, literalFromNode, opts);
}
+
+ /* ── NEW: resolve Identifier bindings recursively ─ */
+ if (t.isIdentifier(value)) {
+ const idBinding = path.scope.getBinding(value.name);
+ if (!idBinding || !idBinding.path.isVariableDeclarator()) return null;
+ const resolvedInit = idBinding.path.node.init;
+ if (!resolvedInit) return null;
+ return literalFromNode(resolvedInit as t.Expression, idBinding.path, opts);
+ }
+ VERBOSE && console.log('resolveMemberExpression -> 460');
return null;
}
diff --git a/packages/eslint-plugin-react-zero-ui/__tests__/fixtures/next/app/ChildComponent.tsx b/packages/eslint-plugin-react-zero-ui/__tests__/fixtures/next/app/ChildComponent.tsx
index 6808d9c..2e87ce0 100644
--- a/packages/eslint-plugin-react-zero-ui/__tests__/fixtures/next/app/ChildComponent.tsx
+++ b/packages/eslint-plugin-react-zero-ui/__tests__/fixtures/next/app/ChildComponent.tsx
@@ -1,6 +1,6 @@
-import { type ScopedSetterFn } from '@react-zero-ui/core';
+import { type GlobalSetterFn } from '@react-zero-ui/core';
-export function ChildComponent({ setIsOpen }: { setIsOpen: ScopedSetterFn }) {
+export function ChildComponent({ setIsOpen }: { setIsOpen: GlobalSetterFn<'open' | 'closed'> }) {
return (