zyzz 0.0.18 → 0.0.20

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 (120) hide show
  1. package/README.md +9 -9
  2. package/dist/Config.d.ts +1 -0
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Vars.d.ts +22 -15
  6. package/dist/Vars.d.ts.map +1 -1
  7. package/dist/Vars.js +26 -0
  8. package/dist/Vars.js.map +1 -1
  9. package/dist/cli/index.js +5 -0
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/compiler/Source.d.ts.map +1 -1
  12. package/dist/compiler/Source.js +3 -0
  13. package/dist/compiler/Source.js.map +1 -1
  14. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  15. package/dist/compiler/internal/Configurations.js +3 -0
  16. package/dist/compiler/internal/Configurations.js.map +1 -1
  17. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  18. package/dist/compiler/internal/Contract.js +30 -4
  19. package/dist/compiler/internal/Contract.js.map +1 -1
  20. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  21. package/dist/compiler/internal/Themes.js +83 -23
  22. package/dist/compiler/internal/Themes.js.map +1 -1
  23. package/dist/default.d.ts +7 -11
  24. package/dist/default.d.ts.map +1 -1
  25. package/dist/default.js +4 -5
  26. package/dist/default.js.map +2 -2
  27. package/dist/default.js.zyzz.json +1 -1
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +15 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/internal/Token.d.ts +11 -2
  33. package/dist/internal/Token.d.ts.map +1 -1
  34. package/dist/internal/Token.js +2 -0
  35. package/dist/internal/Token.js.map +1 -1
  36. package/dist/internal/VariableSets.d.ts.map +1 -1
  37. package/dist/internal/VariableSets.js +18 -1
  38. package/dist/internal/VariableSets.js.map +1 -1
  39. package/dist/node/Host.d.ts +2 -0
  40. package/dist/node/Host.d.ts.map +1 -1
  41. package/dist/node/Host.js +7 -0
  42. package/dist/node/Host.js.map +1 -1
  43. package/dist/oxlint/index.d.ts +6 -0
  44. package/dist/oxlint/index.d.ts.map +1 -0
  45. package/dist/oxlint/index.js +18 -0
  46. package/dist/oxlint/index.js.map +1 -0
  47. package/dist/oxlint/internal/Bindings.d.ts +20 -0
  48. package/dist/oxlint/internal/Bindings.d.ts.map +1 -0
  49. package/dist/oxlint/internal/Bindings.js +203 -0
  50. package/dist/oxlint/internal/Bindings.js.map +1 -0
  51. package/dist/oxlint/internal/Declarations.d.ts +19 -0
  52. package/dist/oxlint/internal/Declarations.d.ts.map +1 -0
  53. package/dist/oxlint/internal/Declarations.js +114 -0
  54. package/dist/oxlint/internal/Declarations.js.map +1 -0
  55. package/dist/oxlint/rules/noConflictingProps.d.ts +5 -0
  56. package/dist/oxlint/rules/noConflictingProps.d.ts.map +1 -0
  57. package/dist/oxlint/rules/noConflictingProps.js +45 -0
  58. package/dist/oxlint/rules/noConflictingProps.js.map +1 -0
  59. package/dist/oxlint/rules/noUnused.d.ts +5 -0
  60. package/dist/oxlint/rules/noUnused.d.ts.map +1 -0
  61. package/dist/oxlint/rules/noUnused.js +62 -0
  62. package/dist/oxlint/rules/noUnused.js.map +1 -0
  63. package/dist/oxlint/rules/restrictedProperties.d.ts +5 -0
  64. package/dist/oxlint/rules/restrictedProperties.d.ts.map +1 -0
  65. package/dist/oxlint/rules/restrictedProperties.js +82 -0
  66. package/dist/oxlint/rules/restrictedProperties.js.map +1 -0
  67. package/dist/oxlint/rules/useLogicalProperties.d.ts +5 -0
  68. package/dist/oxlint/rules/useLogicalProperties.d.ts.map +1 -0
  69. package/dist/oxlint/rules/useLogicalProperties.js +76 -0
  70. package/dist/oxlint/rules/useLogicalProperties.js.map +1 -0
  71. package/dist/oxlint/rules/validStyles.d.ts +5 -0
  72. package/dist/oxlint/rules/validStyles.d.ts.map +1 -0
  73. package/dist/oxlint/rules/validStyles.js +75 -0
  74. package/dist/oxlint/rules/validStyles.js.map +1 -0
  75. package/dist/react-native/StyleSheet.js +2 -0
  76. package/dist/react-native/StyleSheet.js.map +1 -1
  77. package/dist/runtime/Composition.d.ts +1 -1
  78. package/dist/runtime/ConditionalRecipe.d.ts +1 -1
  79. package/dist/runtime/PayloadRecipe.d.ts +1 -1
  80. package/dist/runtime/Recipe.d.ts +1 -1
  81. package/dist/styleFunction.d.ts +1 -1
  82. package/dist/styleFunction.d.ts.map +1 -1
  83. package/dist/styleFunction.js.map +1 -1
  84. package/dist/variants.d.ts +14 -6
  85. package/dist/variants.d.ts.map +1 -1
  86. package/dist/variants.js.map +1 -1
  87. package/dist/vite/index.d.ts +2 -0
  88. package/dist/vite/index.d.ts.map +1 -1
  89. package/dist/vite/index.js +75 -30
  90. package/dist/vite/index.js.map +1 -1
  91. package/dist/web/internal/Themes.d.ts.map +1 -1
  92. package/dist/web/internal/Themes.js +4 -0
  93. package/dist/web/internal/Themes.js.map +1 -1
  94. package/package.json +7 -1
  95. package/src/Config.ts +5 -0
  96. package/src/Vars.ts +88 -29
  97. package/src/cli/index.ts +5 -0
  98. package/src/compiler/Source.ts +3 -0
  99. package/src/compiler/internal/Configurations.ts +3 -0
  100. package/src/compiler/internal/Contract.ts +47 -4
  101. package/src/compiler/internal/Themes.ts +109 -34
  102. package/src/default.ts +8 -10
  103. package/src/index.ts +15 -0
  104. package/src/internal/Authoring.ts +1 -1
  105. package/src/internal/Token.ts +39 -11
  106. package/src/internal/VariableSets.ts +25 -1
  107. package/src/node/Host.ts +19 -0
  108. package/src/oxlint/index.ts +21 -0
  109. package/src/oxlint/internal/Bindings.ts +270 -0
  110. package/src/oxlint/internal/Declarations.ts +137 -0
  111. package/src/oxlint/rules/noConflictingProps.ts +52 -0
  112. package/src/oxlint/rules/noUnused.ts +67 -0
  113. package/src/oxlint/rules/restrictedProperties.ts +92 -0
  114. package/src/oxlint/rules/useLogicalProperties.ts +81 -0
  115. package/src/oxlint/rules/validStyles.ts +88 -0
  116. package/src/react-native/StyleSheet.ts +6 -0
  117. package/src/styleFunction.ts +1 -3
  118. package/src/variants.ts +38 -16
  119. package/src/vite/index.ts +94 -33
  120. package/src/web/internal/Themes.ts +4 -0
@@ -0,0 +1,67 @@
1
+ /** Finds unused style members in local TypeScript namespaces. @module */
2
+ import type { ESTree, Rule } from '@oxlint/plugins'
3
+ import * as Bindings from '../internal/Bindings.js'
4
+
5
+ /** Leaves module exports and escaped namespaces available to unknown consumers. */
6
+ export const noUnused: Rule = {
7
+ meta: {
8
+ messages: { unused: "Style '{{name}}' is never used." },
9
+ schema: [],
10
+ type: 'suggestion',
11
+ },
12
+ create(context) {
13
+ const bindings = Bindings.create(context)
14
+ const declarations: ESTree.VariableDeclarator[] = []
15
+ return {
16
+ VariableDeclarator(node) {
17
+ if (
18
+ node.id.type === 'Identifier' &&
19
+ node.init &&
20
+ bindings.resolve(node.init)?.kind === 'definition'
21
+ )
22
+ declarations.push(node)
23
+ },
24
+ 'Program:exit'() {
25
+ for (const declaration of declarations) {
26
+ if (declaration.id.type !== 'Identifier') continue
27
+ let parent: ESTree.Node = declaration.parent
28
+ while (
29
+ parent.type === 'VariableDeclaration' ||
30
+ parent.type === 'ExportNamedDeclaration'
31
+ )
32
+ parent = parent.parent
33
+ if (
34
+ parent.type !== 'TSModuleBlock' ||
35
+ parent.parent.type !== 'TSModuleDeclaration'
36
+ )
37
+ continue
38
+ const namespace = parent.parent
39
+ if (namespace.id.type !== 'Identifier') continue
40
+ // Nested and exported namespaces may have consumers outside this local scope.
41
+ if (namespace.parent.type !== 'Program') continue
42
+ const variable = bindings.variable(declaration.id)
43
+ const owner = bindings.variable(namespace.id)
44
+ if (!variable || !owner || owner.defs.length !== 1) continue
45
+ if (variable.references.some((reference) => reference.isRead()))
46
+ continue
47
+ const used = owner.references.some((reference) => {
48
+ if (!reference.isRead()) return false
49
+ const member = reference.identifier.parent
50
+ return (
51
+ member.type !== 'MemberExpression' ||
52
+ member.object !== reference.identifier ||
53
+ Bindings.key(member) === undefined ||
54
+ Bindings.key(member) === variable.name
55
+ )
56
+ })
57
+ if (!used)
58
+ context.report({
59
+ data: { name: `${namespace.id.name}.${variable.name}` },
60
+ messageId: 'unused',
61
+ node: declaration.id,
62
+ })
63
+ }
64
+ },
65
+ }
66
+ },
67
+ }
@@ -0,0 +1,92 @@
1
+ /** Applies explicit project restrictions to known web declarations. @module */
2
+ import type { Rule } from '@oxlint/plugins'
3
+ import * as Bindings from '../internal/Bindings.js'
4
+ import * as Declarations from '../internal/Declarations.js'
5
+
6
+ /** Reports forbidden properties or literal values outside an explicit allowlist. */
7
+ export const restrictedProperties: Rule = {
8
+ meta: {
9
+ messages: {
10
+ restricted: "Property '{{property}}' is restricted. {{reason}}",
11
+ },
12
+ schema: [
13
+ {
14
+ additionalProperties: {
15
+ additionalProperties: false,
16
+ properties: {
17
+ reason: { type: 'string' },
18
+ values: { items: { type: ['string', 'number'] }, type: 'array' },
19
+ },
20
+ type: 'object',
21
+ },
22
+ type: 'object',
23
+ },
24
+ ],
25
+ type: 'suggestion',
26
+ },
27
+ create(context) {
28
+ const bindings = Bindings.create(context)
29
+ const restrictions = context.options[0]
30
+ const reported = new Set<number>()
31
+ return {
32
+ CallExpression(node) {
33
+ if (
34
+ !restrictions ||
35
+ typeof restrictions !== 'object' ||
36
+ Array.isArray(restrictions)
37
+ )
38
+ return
39
+ const binding = bindings.resolve(node.callee)
40
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants') return
41
+ const input = node.arguments[0]
42
+ if (!input || input.type === 'SpreadElement') return
43
+ Declarations.visit(input, {
44
+ bindings,
45
+ recipe: binding.kind === 'variants',
46
+ visitors: {
47
+ declaration(property, name) {
48
+ if (
49
+ !Object.hasOwn(restrictions, name) ||
50
+ reported.has(property.start)
51
+ )
52
+ return
53
+ const restriction = restrictions[name]
54
+ if (
55
+ !restriction ||
56
+ typeof restriction !== 'object' ||
57
+ Array.isArray(restriction)
58
+ )
59
+ return
60
+ const allowed = restriction.values
61
+ if (Array.isArray(allowed)) {
62
+ const literal = Declarations.literal(property.value, bindings)
63
+ if (!literal) return
64
+ const values: unknown[] = Array.isArray(literal.value)
65
+ ? literal.value
66
+ : [literal.value]
67
+ if (
68
+ values.every((value) =>
69
+ allowed.some((allowed) => allowed === value),
70
+ )
71
+ )
72
+ return
73
+ }
74
+ reported.add(property.start)
75
+ context.report({
76
+ data: {
77
+ property: name,
78
+ reason:
79
+ typeof restriction.reason === 'string'
80
+ ? restriction.reason
81
+ : 'Use an approved property or value.',
82
+ },
83
+ messageId: 'restricted',
84
+ node: property.key,
85
+ })
86
+ },
87
+ },
88
+ })
89
+ },
90
+ }
91
+ },
92
+ }
@@ -0,0 +1,81 @@
1
+ /** Encourages logical CSS properties in web style declarations. @module */
2
+ import type { Rule } from '@oxlint/plugins'
3
+ import * as Bindings from '../internal/Bindings.js'
4
+ import * as Declarations from '../internal/Declarations.js'
5
+
6
+ const replacements: Readonly<Record<string, string>> = {
7
+ borderBottomLeftRadius: 'borderEndStartRadius',
8
+ borderBottomRightRadius: 'borderEndEndRadius',
9
+ borderLeft: 'borderInlineStart',
10
+ borderLeftColor: 'borderInlineStartColor',
11
+ borderLeftStyle: 'borderInlineStartStyle',
12
+ borderLeftWidth: 'borderInlineStartWidth',
13
+ borderRight: 'borderInlineEnd',
14
+ borderRightColor: 'borderInlineEndColor',
15
+ borderRightStyle: 'borderInlineEndStyle',
16
+ borderRightWidth: 'borderInlineEndWidth',
17
+ borderTopLeftRadius: 'borderStartStartRadius',
18
+ borderTopRightRadius: 'borderStartEndRadius',
19
+ insetLeft: 'insetInlineStart',
20
+ insetRight: 'insetInlineEnd',
21
+ left: 'insetInlineStart',
22
+ marginLeft: 'marginInlineStart',
23
+ marginRight: 'marginInlineEnd',
24
+ paddingLeft: 'paddingInlineStart',
25
+ paddingRight: 'paddingInlineEnd',
26
+ right: 'insetInlineEnd',
27
+ scrollMarginLeft: 'scrollMarginInlineStart',
28
+ scrollMarginRight: 'scrollMarginInlineEnd',
29
+ scrollPaddingLeft: 'scrollPaddingInlineStart',
30
+ scrollPaddingRight: 'scrollPaddingInlineEnd',
31
+ }
32
+
33
+ /** Reports physical properties without changing direction-dependent behavior. */
34
+ export const useLogicalProperties: Rule = {
35
+ meta: {
36
+ messages: {
37
+ logical:
38
+ "Use a CSS logical property instead of '{{property}}', such as '{{replacement}}' for horizontal LTR layouts.",
39
+ },
40
+ schema: [],
41
+ type: 'suggestion',
42
+ },
43
+ create(context) {
44
+ const bindings = Bindings.create(context)
45
+ const reported = new Set<number>()
46
+ return {
47
+ CallExpression(node) {
48
+ const binding = bindings.resolve(node.callee)
49
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants') return
50
+ const input = node.arguments[0]
51
+ if (!input || input.type === 'SpreadElement') return
52
+ Declarations.visit(input, {
53
+ bindings,
54
+ recipe: binding.kind === 'variants',
55
+ visitors: {
56
+ declaration(property, name) {
57
+ const replacement = Object.hasOwn(replacements, name)
58
+ ? replacements[name]
59
+ : undefined
60
+ if (!replacement || reported.has(property.start)) return
61
+ if (
62
+ context.sourceCode
63
+ .getCommentsBefore(property)
64
+ .some((comment) =>
65
+ comment.value.includes('allow-physical-property'),
66
+ )
67
+ )
68
+ return
69
+ reported.add(property.start)
70
+ context.report({
71
+ data: { property: name, replacement },
72
+ messageId: 'logical',
73
+ node: property.key,
74
+ })
75
+ },
76
+ },
77
+ })
78
+ },
79
+ }
80
+ },
81
+ }
@@ -0,0 +1,88 @@
1
+ /** Reports statically known declaration errors using Zyzz's authoring validation. @module */
2
+ import type { ESTree, Rule } from '@oxlint/plugins'
3
+ import * as Bindings from '../internal/Bindings.js'
4
+ import * as Declarations from '../internal/Declarations.js'
5
+ import * as Literal from '../../internal/Literal.js'
6
+ import * as Style from '../../Style.js'
7
+
8
+ /** Validates known structure and values while leaving unresolved bindings to the compiler. */
9
+ export const validStyles: Rule = {
10
+ meta: { messages: { invalid: '{{message}}' }, schema: [], type: 'problem' },
11
+ create(context) {
12
+ const bindings = Bindings.create(context)
13
+ const reported = new Set<number>()
14
+ function report(node: ESTree.Node, message: string) {
15
+ if (reported.has(node.start)) return
16
+ reported.add(node.start)
17
+ context.report({ data: { message }, messageId: 'invalid', node })
18
+ }
19
+ return {
20
+ CallExpression(node) {
21
+ const binding = bindings.resolve(node.callee)
22
+ if (binding?.kind !== 'style' && binding?.kind !== 'variants') return
23
+ const input = node.arguments[0]
24
+ if (!input || input.type === 'SpreadElement') return
25
+ Declarations.visit(input, {
26
+ bindings,
27
+ recipe: binding.kind === 'variants',
28
+ visitors: {
29
+ declaration(property, name) {
30
+ if (binding.themed && name === 'typography') return
31
+ if (
32
+ !binding.themed &&
33
+ !name.startsWith('--') &&
34
+ !Object.hasOwn(Literal.rules, name)
35
+ ) {
36
+ report(property.key, `Unknown CSS property '${name}'.`)
37
+ return
38
+ }
39
+ const value = bindings.expression(property.value)
40
+ if (value.type === 'ObjectExpression') {
41
+ report(
42
+ value,
43
+ 'CSS declarations require scalar values or fallback arrays.',
44
+ )
45
+ return
46
+ }
47
+ if (
48
+ value.type === 'ArrayExpression' &&
49
+ value.elements.some((element) => !element)
50
+ ) {
51
+ report(
52
+ value,
53
+ 'Fallback arrays require dense data entries without holes.',
54
+ )
55
+ return
56
+ }
57
+ const literal = Declarations.literal(value, bindings)
58
+ if (!literal) return
59
+ const values = Array.isArray(literal.value)
60
+ ? literal.value
61
+ : [literal.value]
62
+ if (
63
+ values.some(
64
+ (value) =>
65
+ typeof value !== 'string' && typeof value !== 'number',
66
+ )
67
+ ) {
68
+ report(
69
+ value,
70
+ 'CSS declarations require string or number values.',
71
+ )
72
+ return
73
+ }
74
+ try {
75
+ Style.define({ lint: { [name]: literal.value } } as never)
76
+ } catch (error) {
77
+ if (!(error instanceof Style.InvalidError)) throw error
78
+ for (const diagnostic of error.diagnostics)
79
+ report(value, diagnostic.message)
80
+ }
81
+ },
82
+ invalid: report,
83
+ },
84
+ })
85
+ },
86
+ }
87
+ },
88
+ }
@@ -785,6 +785,12 @@ function resolve(
785
785
  resolved = resolved.value
786
786
  continue
787
787
  }
788
+ if (Token.isExpression(resolved))
789
+ fail(
790
+ 'unsupported_feature',
791
+ 'Composed variables require a web target.',
792
+ path,
793
+ )
788
794
  if ('default' in resolved)
789
795
  fail(
790
796
  'unsupported_feature',
@@ -114,9 +114,7 @@ export declare namespace style {
114
114
  /** Compiled and supplied class names. */
115
115
  readonly className: string
116
116
  /** Supplied inline styling overrides when present. */
117
- readonly style?:
118
- | Readonly<Record<string, string | number | undefined>>
119
- | undefined
117
+ readonly style?: Readonly<Record<string, string | number | undefined>>
120
118
  }
121
119
 
122
120
  /** Callable definition; source rewriting supplies its implementation. */
package/src/variants.ts CHANGED
@@ -61,32 +61,54 @@ type CheckedChoice<
61
61
  : never)
62
62
  : CheckedStyles<style, tokens, layers, mappings>
63
63
 
64
- type SuggestedStyles<tokens extends Theme.Tokens> =
65
- Completion.Properties<tokens> & Readonly<Record<string, unknown>>
64
+ type SuggestedStyles<
65
+ tokens extends Theme.Tokens,
66
+ styles = Record<string, unknown>,
67
+ > = Completion.Properties<tokens, styles> & Readonly<Record<string, unknown>>
66
68
 
67
69
  /** Editor hints remain independent of declaration and selection validation. */
68
- type Suggestions<tokens extends Theme.Tokens> = {
69
- readonly base?: SuggestedStyles<tokens> | undefined
70
+ type Suggestions<
71
+ tokens extends Theme.Tokens,
72
+ definition = Record<string, unknown>,
73
+ > = {
74
+ readonly base?:
75
+ | SuggestedStyles<
76
+ tokens,
77
+ definition extends { base: infer styles }
78
+ ? styles
79
+ : Record<string, unknown>
80
+ >
81
+ | undefined
70
82
  readonly compoundVariants?:
71
83
  | readonly {
72
84
  readonly style: SuggestedStyles<tokens>
73
85
  readonly when: unknown
74
86
  }[]
75
87
  | undefined
76
- readonly variants?:
77
- | Readonly<
78
- Record<
79
- string,
80
- Readonly<
88
+ readonly variants?: definition extends { variants: infer axes }
89
+ ? {
90
+ readonly [axis in keyof axes]: {
91
+ readonly [choice in keyof axes[axis]]: axes[axis][choice] extends (
92
+ ...args: never[]
93
+ ) => unknown
94
+ ? (...args: never[]) => SuggestedStyles<tokens>
95
+ : SuggestedStyles<tokens, axes[axis][choice]>
96
+ }
97
+ }
98
+ :
99
+ | Readonly<
81
100
  Record<
82
101
  string,
83
- | SuggestedStyles<tokens>
84
- | ((...args: never[]) => SuggestedStyles<tokens>)
102
+ Readonly<
103
+ Record<
104
+ string,
105
+ | SuggestedStyles<tokens>
106
+ | ((...args: never[]) => SuggestedStyles<tokens>)
107
+ >
108
+ >
85
109
  >
86
110
  >
87
- >
88
- >
89
- | undefined
111
+ | undefined
90
112
  }
91
113
 
92
114
  type Keys<value> = value extends unknown ? keyof value : never
@@ -201,7 +223,7 @@ type Checked<
201
223
  export function variants<const definition extends Record<string, unknown>>(
202
224
  definition: definition &
203
225
  NoInfer<Checked<definition, {}, never, {}>> &
204
- Suggestions<{}>,
226
+ Suggestions<{}, NoInfer<definition>>,
205
227
  options: style.DefinitionOptions = {},
206
228
  ): variants.ReturnType<definition> {
207
229
  return Authoring.variants(
@@ -221,7 +243,7 @@ export declare namespace variants {
221
243
  > = <const definition extends Record<string, unknown>>(
222
244
  definition: definition &
223
245
  NoInfer<Checked<definition, tokens, layers, mappings>> &
224
- Suggestions<tokens>,
246
+ Suggestions<tokens, NoInfer<definition>>,
225
247
  options?: style.DefinitionOptions,
226
248
  ) => ReturnType<definition, output>
227
249