zyzz 0.0.18 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/oxlint/index.d.ts +6 -0
  2. package/dist/oxlint/index.d.ts.map +1 -0
  3. package/dist/oxlint/index.js +18 -0
  4. package/dist/oxlint/index.js.map +1 -0
  5. package/dist/oxlint/internal/Bindings.d.ts +20 -0
  6. package/dist/oxlint/internal/Bindings.d.ts.map +1 -0
  7. package/dist/oxlint/internal/Bindings.js +203 -0
  8. package/dist/oxlint/internal/Bindings.js.map +1 -0
  9. package/dist/oxlint/internal/Declarations.d.ts +19 -0
  10. package/dist/oxlint/internal/Declarations.d.ts.map +1 -0
  11. package/dist/oxlint/internal/Declarations.js +114 -0
  12. package/dist/oxlint/internal/Declarations.js.map +1 -0
  13. package/dist/oxlint/rules/noConflictingProps.d.ts +5 -0
  14. package/dist/oxlint/rules/noConflictingProps.d.ts.map +1 -0
  15. package/dist/oxlint/rules/noConflictingProps.js +45 -0
  16. package/dist/oxlint/rules/noConflictingProps.js.map +1 -0
  17. package/dist/oxlint/rules/noUnused.d.ts +5 -0
  18. package/dist/oxlint/rules/noUnused.d.ts.map +1 -0
  19. package/dist/oxlint/rules/noUnused.js +62 -0
  20. package/dist/oxlint/rules/noUnused.js.map +1 -0
  21. package/dist/oxlint/rules/restrictedProperties.d.ts +5 -0
  22. package/dist/oxlint/rules/restrictedProperties.d.ts.map +1 -0
  23. package/dist/oxlint/rules/restrictedProperties.js +82 -0
  24. package/dist/oxlint/rules/restrictedProperties.js.map +1 -0
  25. package/dist/oxlint/rules/useLogicalProperties.d.ts +5 -0
  26. package/dist/oxlint/rules/useLogicalProperties.d.ts.map +1 -0
  27. package/dist/oxlint/rules/useLogicalProperties.js +76 -0
  28. package/dist/oxlint/rules/useLogicalProperties.js.map +1 -0
  29. package/dist/oxlint/rules/validStyles.d.ts +5 -0
  30. package/dist/oxlint/rules/validStyles.d.ts.map +1 -0
  31. package/dist/oxlint/rules/validStyles.js +75 -0
  32. package/dist/oxlint/rules/validStyles.js.map +1 -0
  33. package/package.json +7 -1
  34. package/src/oxlint/index.ts +21 -0
  35. package/src/oxlint/internal/Bindings.ts +270 -0
  36. package/src/oxlint/internal/Declarations.ts +137 -0
  37. package/src/oxlint/rules/noConflictingProps.ts +52 -0
  38. package/src/oxlint/rules/noUnused.ts +67 -0
  39. package/src/oxlint/rules/restrictedProperties.ts +92 -0
  40. package/src/oxlint/rules/useLogicalProperties.ts +81 -0
  41. package/src/oxlint/rules/validStyles.ts +88 -0
@@ -0,0 +1,82 @@
1
+ import * as Bindings from '../internal/Bindings.js';
2
+ import * as Declarations from '../internal/Declarations.js';
3
+ /** Reports forbidden properties or literal values outside an explicit allowlist. */
4
+ export const restrictedProperties = {
5
+ meta: {
6
+ messages: {
7
+ restricted: "Property '{{property}}' is restricted. {{reason}}",
8
+ },
9
+ schema: [
10
+ {
11
+ additionalProperties: {
12
+ additionalProperties: false,
13
+ properties: {
14
+ reason: { type: 'string' },
15
+ values: { items: { type: ['string', 'number'] }, type: 'array' },
16
+ },
17
+ type: 'object',
18
+ },
19
+ type: 'object',
20
+ },
21
+ ],
22
+ type: 'suggestion',
23
+ },
24
+ create(context) {
25
+ const bindings = Bindings.create(context);
26
+ const restrictions = context.options[0];
27
+ const reported = new Set();
28
+ return {
29
+ CallExpression(node) {
30
+ if (!restrictions ||
31
+ typeof restrictions !== 'object' ||
32
+ Array.isArray(restrictions))
33
+ return;
34
+ const binding = bindings.resolve(node.callee);
35
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants')
36
+ return;
37
+ const input = node.arguments[0];
38
+ if (!input || input.type === 'SpreadElement')
39
+ return;
40
+ Declarations.visit(input, {
41
+ bindings,
42
+ recipe: binding.kind === 'variants',
43
+ visitors: {
44
+ declaration(property, name) {
45
+ if (!Object.hasOwn(restrictions, name) ||
46
+ reported.has(property.start))
47
+ return;
48
+ const restriction = restrictions[name];
49
+ if (!restriction ||
50
+ typeof restriction !== 'object' ||
51
+ Array.isArray(restriction))
52
+ return;
53
+ const allowed = restriction.values;
54
+ if (Array.isArray(allowed)) {
55
+ const literal = Declarations.literal(property.value, bindings);
56
+ if (!literal)
57
+ return;
58
+ const values = Array.isArray(literal.value)
59
+ ? literal.value
60
+ : [literal.value];
61
+ if (values.every((value) => allowed.some((allowed) => allowed === value)))
62
+ return;
63
+ }
64
+ reported.add(property.start);
65
+ context.report({
66
+ data: {
67
+ property: name,
68
+ reason: typeof restriction.reason === 'string'
69
+ ? restriction.reason
70
+ : 'Use an approved property or value.',
71
+ },
72
+ messageId: 'restricted',
73
+ node: property.key,
74
+ });
75
+ },
76
+ },
77
+ });
78
+ },
79
+ };
80
+ },
81
+ };
82
+ //# sourceMappingURL=restrictedProperties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restrictedProperties.js","sourceRoot":"","sources":["../../../src/oxlint/rules/restrictedProperties.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Encourages logical CSS properties in web style declarations. @module */
2
+ import type { Rule } from '@oxlint/plugins';
3
+ /** Reports physical properties without changing direction-dependent behavior. */
4
+ export declare const useLogicalProperties: Rule;
5
+ //# sourceMappingURL=useLogicalProperties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLogicalProperties.d.ts","sourceRoot":"","sources":["../../../src/oxlint/rules/useLogicalProperties.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AA+B3C,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,EAAE,IA+ClC,CAAA"}
@@ -0,0 +1,76 @@
1
+ import * as Bindings from '../internal/Bindings.js';
2
+ import * as Declarations from '../internal/Declarations.js';
3
+ const replacements = {
4
+ borderBottomLeftRadius: 'borderEndStartRadius',
5
+ borderBottomRightRadius: 'borderEndEndRadius',
6
+ borderLeft: 'borderInlineStart',
7
+ borderLeftColor: 'borderInlineStartColor',
8
+ borderLeftStyle: 'borderInlineStartStyle',
9
+ borderLeftWidth: 'borderInlineStartWidth',
10
+ borderRight: 'borderInlineEnd',
11
+ borderRightColor: 'borderInlineEndColor',
12
+ borderRightStyle: 'borderInlineEndStyle',
13
+ borderRightWidth: 'borderInlineEndWidth',
14
+ borderTopLeftRadius: 'borderStartStartRadius',
15
+ borderTopRightRadius: 'borderStartEndRadius',
16
+ insetLeft: 'insetInlineStart',
17
+ insetRight: 'insetInlineEnd',
18
+ left: 'insetInlineStart',
19
+ marginLeft: 'marginInlineStart',
20
+ marginRight: 'marginInlineEnd',
21
+ paddingLeft: 'paddingInlineStart',
22
+ paddingRight: 'paddingInlineEnd',
23
+ right: 'insetInlineEnd',
24
+ scrollMarginLeft: 'scrollMarginInlineStart',
25
+ scrollMarginRight: 'scrollMarginInlineEnd',
26
+ scrollPaddingLeft: 'scrollPaddingInlineStart',
27
+ scrollPaddingRight: 'scrollPaddingInlineEnd',
28
+ };
29
+ /** Reports physical properties without changing direction-dependent behavior. */
30
+ export const useLogicalProperties = {
31
+ meta: {
32
+ messages: {
33
+ logical: "Use a CSS logical property instead of '{{property}}', such as '{{replacement}}' for horizontal LTR layouts.",
34
+ },
35
+ schema: [],
36
+ type: 'suggestion',
37
+ },
38
+ create(context) {
39
+ const bindings = Bindings.create(context);
40
+ const reported = new Set();
41
+ return {
42
+ CallExpression(node) {
43
+ const binding = bindings.resolve(node.callee);
44
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants')
45
+ return;
46
+ const input = node.arguments[0];
47
+ if (!input || input.type === 'SpreadElement')
48
+ return;
49
+ Declarations.visit(input, {
50
+ bindings,
51
+ recipe: binding.kind === 'variants',
52
+ visitors: {
53
+ declaration(property, name) {
54
+ const replacement = Object.hasOwn(replacements, name)
55
+ ? replacements[name]
56
+ : undefined;
57
+ if (!replacement || reported.has(property.start))
58
+ return;
59
+ if (context.sourceCode
60
+ .getCommentsBefore(property)
61
+ .some((comment) => comment.value.includes('allow-physical-property')))
62
+ return;
63
+ reported.add(property.start);
64
+ context.report({
65
+ data: { property: name, replacement },
66
+ messageId: 'logical',
67
+ node: property.key,
68
+ });
69
+ },
70
+ },
71
+ });
72
+ },
73
+ };
74
+ },
75
+ };
76
+ //# sourceMappingURL=useLogicalProperties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLogicalProperties.js","sourceRoot":"","sources":["../../../src/oxlint/rules/useLogicalProperties.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Reports statically known declaration errors using Zyzz's authoring validation. @module */
2
+ import type { Rule } from '@oxlint/plugins';
3
+ /** Validates known structure and values while leaving unresolved bindings to the compiler. */
4
+ export declare const validStyles: Rule;
5
+ //# sourceMappingURL=validStyles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validStyles.d.ts","sourceRoot":"","sources":["../../../src/oxlint/rules/validStyles.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,OAAO,KAAK,EAAU,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAMnD,8FAA8F;AAC9F,eAAO,MAAM,WAAW,EAAE,IA+EzB,CAAA"}
@@ -0,0 +1,75 @@
1
+ import * as Bindings from '../internal/Bindings.js';
2
+ import * as Declarations from '../internal/Declarations.js';
3
+ import * as Literal from '../../internal/Literal.js';
4
+ import * as Style from '../../Style.js';
5
+ /** Validates known structure and values while leaving unresolved bindings to the compiler. */
6
+ export const validStyles = {
7
+ meta: { messages: { invalid: '{{message}}' }, schema: [], type: 'problem' },
8
+ create(context) {
9
+ const bindings = Bindings.create(context);
10
+ const reported = new Set();
11
+ function report(node, message) {
12
+ if (reported.has(node.start))
13
+ return;
14
+ reported.add(node.start);
15
+ context.report({ data: { message }, messageId: 'invalid', node });
16
+ }
17
+ return {
18
+ CallExpression(node) {
19
+ const binding = bindings.resolve(node.callee);
20
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants')
21
+ return;
22
+ const input = node.arguments[0];
23
+ if (!input || input.type === 'SpreadElement')
24
+ return;
25
+ Declarations.visit(input, {
26
+ bindings,
27
+ recipe: binding.kind === 'variants',
28
+ visitors: {
29
+ declaration(property, name) {
30
+ if (binding.themed && name === 'typography')
31
+ return;
32
+ if (!binding.themed &&
33
+ !name.startsWith('--') &&
34
+ !Object.hasOwn(Literal.rules, name)) {
35
+ report(property.key, `Unknown CSS property '${name}'.`);
36
+ return;
37
+ }
38
+ const value = bindings.expression(property.value);
39
+ if (value.type === 'ObjectExpression') {
40
+ report(value, 'CSS declarations require scalar values or fallback arrays.');
41
+ return;
42
+ }
43
+ if (value.type === 'ArrayExpression' &&
44
+ value.elements.some((element) => !element)) {
45
+ report(value, 'Fallback arrays require dense data entries without holes.');
46
+ return;
47
+ }
48
+ const literal = Declarations.literal(value, bindings);
49
+ if (!literal)
50
+ return;
51
+ const values = Array.isArray(literal.value)
52
+ ? literal.value
53
+ : [literal.value];
54
+ if (values.some((value) => typeof value !== 'string' && typeof value !== 'number')) {
55
+ report(value, 'CSS declarations require string or number values.');
56
+ return;
57
+ }
58
+ try {
59
+ Style.define({ lint: { [name]: literal.value } });
60
+ }
61
+ catch (error) {
62
+ if (!(error instanceof Style.InvalidError))
63
+ throw error;
64
+ for (const diagnostic of error.diagnostics)
65
+ report(value, diagnostic.message);
66
+ }
67
+ },
68
+ invalid: report,
69
+ },
70
+ });
71
+ },
72
+ };
73
+ },
74
+ };
75
+ //# sourceMappingURL=validStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validStyles.js","sourceRoot":"","sources":["../../../src/oxlint/rules/validStyles.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyzz",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "license": "MIT",
5
5
  "description": "Modern, universal, simple styling library for the Web and React Native",
6
6
  "repository": {
@@ -78,6 +78,11 @@
78
78
  "types": "./dist/node/index.d.ts",
79
79
  "default": "./dist/node/index.js"
80
80
  },
81
+ "./oxlint": {
82
+ "src": "./src/oxlint/index.ts",
83
+ "types": "./dist/oxlint/index.d.ts",
84
+ "default": "./dist/oxlint/index.js"
85
+ },
81
86
  "./react-native": {
82
87
  "src": "./src/react-native/index.ts",
83
88
  "types": "./dist/react-native/index.d.ts",
@@ -127,6 +132,7 @@
127
132
  "@jridgewell/gen-mapping": "^0.3.13",
128
133
  "@jridgewell/trace-mapping": "^0.3.31",
129
134
  "@oxc-project/types": "0.148.0",
135
+ "@oxlint/plugins": "1.68.0",
130
136
  "css-tree": "3.2.1",
131
137
  "incur": "^0.5.1",
132
138
  "lightningcss": "^1.33.0",
@@ -0,0 +1,21 @@
1
+ /** Exposes opt-in Oxlint rules for Zyzz web authoring. @module */
2
+ import type { Plugin } from '@oxlint/plugins'
3
+ import { noConflictingProps } from './rules/noConflictingProps.js'
4
+ import { noUnused } from './rules/noUnused.js'
5
+ import { restrictedProperties } from './rules/restrictedProperties.js'
6
+ import { useLogicalProperties } from './rules/useLogicalProperties.js'
7
+ import { validStyles } from './rules/validStyles.js'
8
+
9
+ /** Register through jsPlugins and enable individual zyzz/* rules in the lint configuration. */
10
+ const plugin: Plugin = {
11
+ meta: { name: 'zyzz' },
12
+ rules: {
13
+ 'no-conflicting-props': noConflictingProps,
14
+ 'no-unused': noUnused,
15
+ 'restricted-properties': restrictedProperties,
16
+ 'use-logical-properties': useLogicalProperties,
17
+ 'valid-styles': validStyles,
18
+ },
19
+ }
20
+
21
+ export default plugin
@@ -0,0 +1,270 @@
1
+ /** Resolves immutable authoring bindings without loading or evaluating modules. @module */
2
+ import type { Context, ESTree, Variable } from '@oxlint/plugins'
3
+
4
+ /** A recognized helper or locally declared style. */
5
+ export type Binding = {
6
+ readonly kind:
7
+ | 'config'
8
+ | 'configFactory'
9
+ | 'configNamespace'
10
+ | 'cx'
11
+ | 'definition'
12
+ | 'module'
13
+ | 'style'
14
+ | 'variants'
15
+ readonly themed: boolean
16
+ }
17
+
18
+ /** Resolves helpers using lexical scope, configuration filenames, and explicit import sources. */
19
+ export function create(context: Context) {
20
+ const settings = context.settings.zyzz
21
+ const imports = new Set(['zyzz', 'zyzz/default'])
22
+ if (settings && typeof settings === 'object' && !Array.isArray(settings)) {
23
+ const sources = settings.imports
24
+ if (Array.isArray(sources))
25
+ for (const source of sources)
26
+ if (typeof source === 'string') imports.add(source)
27
+ }
28
+
29
+ function variable(
30
+ node: Extract<ESTree.Node, { type: 'Identifier' }>,
31
+ ): Variable | undefined {
32
+ let scope = context.sourceCode.getScope(node)
33
+ while (scope) {
34
+ const found = scope.set.get(node.name)
35
+ if (found) return found
36
+ if (!scope.upper) break
37
+ scope = scope.upper
38
+ }
39
+ return undefined
40
+ }
41
+
42
+ function initializer(
43
+ node: Extract<ESTree.Node, { type: 'Identifier' }>,
44
+ ): ESTree.Node | undefined {
45
+ const binding = variable(node)
46
+ if (!binding || binding.defs.length !== 1) return undefined
47
+ const definition = binding.defs[0]!
48
+ if (definition.node.type !== 'VariableDeclarator') return undefined
49
+ if (
50
+ definition.node.parent.type !== 'VariableDeclaration' ||
51
+ definition.node.parent.kind !== 'const'
52
+ )
53
+ return undefined
54
+ if (
55
+ binding.references.some(
56
+ (reference) => reference.isWrite() && !reference.init,
57
+ )
58
+ )
59
+ return undefined
60
+ if (definition.node.id.type !== 'Identifier') return undefined
61
+ const input = definition.node.init && unwrap(definition.node.init)
62
+ if (
63
+ input?.type === 'ArrayExpression' ||
64
+ input?.type === 'ObjectExpression'
65
+ ) {
66
+ const mutable = binding.references.some((reference) => {
67
+ let value: ESTree.Node = reference.identifier
68
+ while (
69
+ value.parent &&
70
+ ((value.parent.type === 'MemberExpression' &&
71
+ value.parent.object === value) ||
72
+ unwrap(value.parent) === value)
73
+ )
74
+ value = value.parent
75
+ const parent = value.parent
76
+ if (
77
+ parent?.type === 'ArrayExpression' ||
78
+ (parent?.type === 'Property' && parent.value === value)
79
+ )
80
+ return true
81
+ if (parent?.type === 'AssignmentExpression' && parent.left === value)
82
+ return true
83
+ if (
84
+ parent?.type === 'UpdateExpression' ||
85
+ (parent?.type === 'UnaryExpression' && parent.operator === 'delete')
86
+ )
87
+ return true
88
+ if (parent?.type === 'CallExpression') {
89
+ if (parent.callee === value) return true
90
+ const callee = resolve(parent.callee)
91
+ return callee?.kind !== 'style' && callee?.kind !== 'variants'
92
+ }
93
+ return parent?.type === 'VariableDeclarator' && parent.init === value
94
+ })
95
+ if (mutable) return undefined
96
+ }
97
+ return definition.node.init ?? undefined
98
+ }
99
+
100
+ function expression(
101
+ node: ESTree.Node,
102
+ seen = new Set<ESTree.Node>(),
103
+ ): ESTree.Node {
104
+ node = unwrap(node)
105
+ if (seen.has(node)) return node
106
+ seen.add(node)
107
+ if (node.type === 'Identifier') {
108
+ const init = initializer(node)
109
+ if (init) return expression(init, seen)
110
+ }
111
+ return node
112
+ }
113
+
114
+ function member(
115
+ binding: Binding | undefined,
116
+ name: string | undefined,
117
+ ): Binding | undefined {
118
+ if (!binding || !name) return undefined
119
+ if (binding.kind === 'module') {
120
+ if (!binding.themed && name === 'Config')
121
+ return { kind: 'configNamespace', themed: true }
122
+ if (
123
+ name === 'style' ||
124
+ name === 'variants' ||
125
+ (!binding.themed && name === 'cx')
126
+ )
127
+ return { kind: name, themed: binding.themed }
128
+ }
129
+ if (binding.kind === 'configNamespace' && name === 'create')
130
+ return { kind: 'configFactory', themed: true }
131
+ if (binding.kind === 'config' && (name === 'style' || name === 'variants'))
132
+ return { kind: name, themed: true }
133
+ return undefined
134
+ }
135
+
136
+ function resolve(
137
+ node: ESTree.Node,
138
+ seen = new Set<ESTree.Node>(),
139
+ ): Binding | undefined {
140
+ node = unwrap(node)
141
+ if (seen.has(node)) return undefined
142
+ seen.add(node)
143
+
144
+ if (node.type === 'Identifier') {
145
+ const binding = variable(node)
146
+ if (!binding || binding.defs.length !== 1) return undefined
147
+ const definition = binding.defs[0]!
148
+ const declaration = definition.node
149
+ if (definition.type === 'ImportBinding') {
150
+ const parent = declaration.parent
151
+ if (
152
+ parent?.type !== 'ImportDeclaration' ||
153
+ parent.importKind === 'type' ||
154
+ (!imports.has(parent.source.value) &&
155
+ !/(?:^|\/)zyzz\.config\.[^/]+$/.test(parent.source.value))
156
+ )
157
+ return undefined
158
+ const module: Binding = {
159
+ kind: 'module',
160
+ themed: parent.source.value !== 'zyzz',
161
+ }
162
+ if (declaration.type === 'ImportNamespaceSpecifier') return module
163
+ if (
164
+ declaration.type === 'ImportSpecifier' &&
165
+ declaration.importKind !== 'type'
166
+ )
167
+ return member(
168
+ module,
169
+ declaration.imported.type === 'Identifier'
170
+ ? declaration.imported.name
171
+ : declaration.imported.value,
172
+ )
173
+ return undefined
174
+ }
175
+ if (
176
+ declaration.type !== 'VariableDeclarator' ||
177
+ !declaration.init ||
178
+ declaration.parent.type !== 'VariableDeclaration' ||
179
+ declaration.parent.kind !== 'const'
180
+ )
181
+ return undefined
182
+ if (
183
+ binding.references.some(
184
+ (reference) => reference.isWrite() && !reference.init,
185
+ )
186
+ )
187
+ return undefined
188
+ if (declaration.id.type === 'Identifier')
189
+ return resolve(declaration.init, seen)
190
+ if (declaration.id.type === 'ObjectPattern') {
191
+ const property = declaration.id.properties.find(
192
+ (property) =>
193
+ property.type === 'Property' &&
194
+ property.value.type === 'Identifier' &&
195
+ property.value.name === node.name,
196
+ )
197
+ if (property?.type === 'Property')
198
+ return member(resolve(declaration.init, seen), key(property))
199
+ }
200
+ }
201
+
202
+ if (node.type === 'MemberExpression') {
203
+ const name = key(node)
204
+ const known = member(resolve(node.object, new Set(seen)), name)
205
+ if (known) return known
206
+ if (node.object.type !== 'Identifier' || !name) return undefined
207
+ const namespace = variable(node.object)
208
+ if (!namespace || namespace.defs.length !== 1) return undefined
209
+ const declaration = namespace.defs[0]!.node
210
+ if (
211
+ declaration.type !== 'TSModuleDeclaration' ||
212
+ declaration.body?.type !== 'TSModuleBlock'
213
+ )
214
+ return undefined
215
+ for (const statement of declaration.body.body) {
216
+ if (
217
+ statement.type !== 'ExportNamedDeclaration' ||
218
+ statement.declaration?.type !== 'VariableDeclaration' ||
219
+ statement.declaration.kind !== 'const'
220
+ )
221
+ continue
222
+ for (const entry of statement.declaration.declarations)
223
+ if (
224
+ entry.id.type === 'Identifier' &&
225
+ entry.id.name === name &&
226
+ entry.init
227
+ )
228
+ return resolve(entry.init, seen)
229
+ }
230
+ }
231
+
232
+ if (node.type === 'CallExpression') {
233
+ const callee = resolve(node.callee, seen)
234
+ if (callee?.kind === 'configFactory')
235
+ return { kind: 'config', themed: true }
236
+ if (callee?.kind === 'style' || callee?.kind === 'variants')
237
+ return { kind: 'definition', themed: callee.themed }
238
+ }
239
+ return undefined
240
+ }
241
+
242
+ return { expression, resolve, variable }
243
+ }
244
+
245
+ /** Resolves only literal property names, never computed identifiers. */
246
+ export function key(
247
+ node:
248
+ | ESTree.ObjectProperty
249
+ | ESTree.BindingProperty
250
+ | ESTree.MemberExpression,
251
+ ): string | undefined {
252
+ const property = node.type === 'Property' ? node.key : node.property
253
+ if (!node.computed && property.type === 'Identifier') return property.name
254
+ if (property.type === 'Literal' && typeof property.value === 'string')
255
+ return property.value
256
+ return undefined
257
+ }
258
+
259
+ /** Removes type-only wrappers while retaining the original source node. */
260
+ export function unwrap(node: ESTree.Node): ESTree.Node {
261
+ while (
262
+ node.type === 'TSAsExpression' ||
263
+ node.type === 'TSSatisfiesExpression' ||
264
+ node.type === 'TSNonNullExpression' ||
265
+ node.type === 'TSTypeAssertion' ||
266
+ node.type === 'ParenthesizedExpression'
267
+ )
268
+ node = node.expression
269
+ return node
270
+ }