@plumeria/eslint-plugin 19.14.4 → 20.0.0

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.
package/README.md CHANGED
@@ -162,7 +162,7 @@ Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-sty
162
162
 
163
163
  ### no-unknown-css-properties
164
164
 
165
- Disallow unknown CSS properties in camelCase within `css.create`, `css.keyframes`, and `css.viewTransition`.
165
+ Disallow unknown CSS properties in camelCase within `css.create`, `css.keyframes`, `css.viewTransition`, and `css.positionTry`. Inside `css.positionTry`, it also reports properties that `@position-try` does not accept.
166
166
 
167
167
  In `css.create`, a key that holds an object is reported with a hint that nested selectors start with `:`, `[` or `@`.
168
168
 
@@ -197,7 +197,7 @@ Validates CSS pseudo-classes and pseudo-elements inside `css.create()`. It check
197
197
 
198
198
  ### validate-at-rules
199
199
 
200
- Validates at-rules inside `css.create()`. It accepts `@media`, `@container`, `@supports`, `@layer`, and `@scope`, each with a prelude, whether or not a space separates it from the keyword, and supports validation of computed keys when TypeScript is available.
200
+ Validates at-rules inside `css.create()`. It accepts `@media`, `@container`, and `@supports`, each with a prelude, whether or not a space separates it from the keyword, and `@starting-style` on its own, and supports validation of computed keys when TypeScript is available.
201
201
 
202
202
  ## Optional rules
203
203
 
@@ -41,6 +41,7 @@ exports.expandBorderShorthands = {
41
41
  },
42
42
  CallExpression(node) {
43
43
  let isCssProperties = false;
44
+ let isPositionTry = false;
44
45
  if (node.callee.type === 'MemberExpression') {
45
46
  if (node.callee.object.type === 'Identifier' &&
46
47
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -49,8 +50,10 @@ exports.expandBorderShorthands = {
49
50
  : null;
50
51
  if (propertyName === 'create' ||
51
52
  propertyName === 'keyframes' ||
52
- propertyName === 'viewTransition') {
53
+ propertyName === 'viewTransition' ||
54
+ propertyName === 'positionTry') {
53
55
  isCssProperties = true;
56
+ isPositionTry = propertyName === 'positionTry';
54
57
  }
55
58
  }
56
59
  }
@@ -58,13 +61,19 @@ exports.expandBorderShorthands = {
58
61
  const aliasName = plumeriaAliases[node.callee.name];
59
62
  if (aliasName === 'create' ||
60
63
  aliasName === 'keyframes' ||
61
- aliasName === 'viewTransition') {
64
+ aliasName === 'viewTransition' ||
65
+ aliasName === 'positionTry') {
62
66
  isCssProperties = true;
67
+ isPositionTry = aliasName === 'positionTry';
63
68
  }
64
69
  }
65
70
  if (isCssProperties) {
66
71
  node.arguments.forEach((arg) => {
67
72
  if (arg.type === 'ObjectExpression') {
73
+ if (isPositionTry) {
74
+ checkStyleObject(arg);
75
+ return;
76
+ }
68
77
  arg.properties.forEach((prop) => {
69
78
  if (prop.type !== 'Property')
70
79
  return;
@@ -140,6 +140,7 @@ exports.formatProperties = {
140
140
  },
141
141
  CallExpression(node) {
142
142
  let isCssProperties = false;
143
+ let isPositionTry = false;
143
144
  if (node.callee.type === 'MemberExpression') {
144
145
  if (node.callee.object.type === 'Identifier' &&
145
146
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -148,8 +149,10 @@ exports.formatProperties = {
148
149
  : null;
149
150
  if (propertyName === 'create' ||
150
151
  propertyName === 'keyframes' ||
151
- propertyName === 'viewTransition') {
152
+ propertyName === 'viewTransition' ||
153
+ propertyName === 'positionTry') {
152
154
  isCssProperties = true;
155
+ isPositionTry = propertyName === 'positionTry';
153
156
  }
154
157
  }
155
158
  }
@@ -157,13 +160,19 @@ exports.formatProperties = {
157
160
  const alias = plumeriaAliases[node.callee.name];
158
161
  if (alias === 'create' ||
159
162
  alias === 'keyframes' ||
160
- alias === 'viewTransition') {
163
+ alias === 'viewTransition' ||
164
+ alias === 'positionTry') {
161
165
  isCssProperties = true;
166
+ isPositionTry = alias === 'positionTry';
162
167
  }
163
168
  }
164
169
  if (isCssProperties) {
165
170
  node.arguments.forEach((arg) => {
166
171
  if (arg.type === 'ObjectExpression') {
172
+ if (isPositionTry) {
173
+ checkStyleObject(arg);
174
+ return;
175
+ }
167
176
  arg.properties.forEach((prop) => {
168
177
  if (prop.type !== 'Property')
169
178
  return;
@@ -49,6 +49,8 @@ exports.noDestructure = {
49
49
  keyName === 'createTheme' ||
50
50
  keyName === 'keyframes' ||
51
51
  keyName === 'viewTransition' ||
52
+ keyName === 'positionTry' ||
53
+ keyName === 'varType' ||
52
54
  keyName === 'use') {
53
55
  context.report({
54
56
  node: prop,
@@ -80,7 +80,9 @@ exports.noInnerCall = {
80
80
  propertyName === 'createStatic' ||
81
81
  propertyName === 'createTheme' ||
82
82
  propertyName === 'keyframes' ||
83
- propertyName === 'viewTransition') {
83
+ propertyName === 'viewTransition' ||
84
+ propertyName === 'positionTry' ||
85
+ propertyName === 'varType') {
84
86
  context.report({
85
87
  node,
86
88
  messageId: 'noInnerCall',
@@ -7,6 +7,8 @@ const STYLE_APIS = new Set([
7
7
  'createTheme',
8
8
  'keyframes',
9
9
  'viewTransition',
10
+ 'positionTry',
11
+ 'varType',
10
12
  ]);
11
13
  const isReference = (node, parent) => {
12
14
  if (!parent)
@@ -115,6 +115,7 @@ exports.noOrderDependentOverlap = {
115
115
  },
116
116
  CallExpression(node) {
117
117
  let isCssProperties = false;
118
+ let isPositionTry = false;
118
119
  if (node.callee.type === 'MemberExpression') {
119
120
  if (node.callee.object.type === 'Identifier' &&
120
121
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -123,8 +124,10 @@ exports.noOrderDependentOverlap = {
123
124
  : null;
124
125
  if (propertyName === 'create' ||
125
126
  propertyName === 'keyframes' ||
126
- propertyName === 'viewTransition') {
127
+ propertyName === 'viewTransition' ||
128
+ propertyName === 'positionTry') {
127
129
  isCssProperties = true;
130
+ isPositionTry = propertyName === 'positionTry';
128
131
  }
129
132
  }
130
133
  }
@@ -132,13 +135,19 @@ exports.noOrderDependentOverlap = {
132
135
  const aliasName = plumeriaAliases[node.callee.name];
133
136
  if (aliasName === 'create' ||
134
137
  aliasName === 'keyframes' ||
135
- aliasName === 'viewTransition') {
138
+ aliasName === 'viewTransition' ||
139
+ aliasName === 'positionTry') {
136
140
  isCssProperties = true;
141
+ isPositionTry = aliasName === 'positionTry';
137
142
  }
138
143
  }
139
144
  if (isCssProperties) {
140
145
  node.arguments.forEach((arg) => {
141
146
  if (arg.type === 'ObjectExpression') {
147
+ if (isPositionTry) {
148
+ checkStyleObject(arg);
149
+ return;
150
+ }
142
151
  arg.properties.forEach((prop) => {
143
152
  if (prop.type !== 'Property')
144
153
  return;
@@ -5,15 +5,57 @@ const zss_engine_1 = require("zss-engine");
5
5
  const styleObject_1 = require("../util/styleObject");
6
6
  const cssPropertyData_1 = require("../util/cssPropertyData");
7
7
  const knownProperties = new Set(cssPropertyData_1.cssPropertyData);
8
+ const positionTryProperties = new Set([
9
+ 'position-anchor',
10
+ 'position-area',
11
+ 'inset',
12
+ 'inset-block',
13
+ 'inset-block-start',
14
+ 'inset-block-end',
15
+ 'inset-inline',
16
+ 'inset-inline-start',
17
+ 'inset-inline-end',
18
+ 'top',
19
+ 'right',
20
+ 'bottom',
21
+ 'left',
22
+ 'place-self',
23
+ 'align-self',
24
+ 'justify-self',
25
+ 'inline-size',
26
+ 'min-inline-size',
27
+ 'max-inline-size',
28
+ 'width',
29
+ 'min-width',
30
+ 'max-width',
31
+ 'block-size',
32
+ 'min-block-size',
33
+ 'max-block-size',
34
+ 'height',
35
+ 'min-height',
36
+ 'max-height',
37
+ 'margin',
38
+ 'margin-block',
39
+ 'margin-block-start',
40
+ 'margin-block-end',
41
+ 'margin-inline',
42
+ 'margin-inline-start',
43
+ 'margin-inline-end',
44
+ 'margin-top',
45
+ 'margin-right',
46
+ 'margin-bottom',
47
+ 'margin-left',
48
+ ]);
8
49
  exports.noUnknownCssProperties = {
9
50
  meta: {
10
51
  type: 'problem',
11
52
  docs: {
12
- description: 'Disallow unknown CSS properties in camelCase within css.create, css.keyframes, and css.viewTransition',
53
+ description: 'Disallow unknown CSS properties in camelCase within css.create, css.keyframes, css.viewTransition, and css.positionTry',
13
54
  },
14
55
  messages: {
15
56
  unknownProperty: "Unknown CSS property '{{ name }}'.",
16
57
  unknownNestedProperty: "Unknown CSS property '{{ name }}'. Nested selectors start with ':', '[' or '@'.",
58
+ positionTryProperty: "'{{ name }}' cannot be used in css.positionTry. @position-try only accepts inset, margin, sizing and self-alignment properties, positionAnchor and positionArea.",
17
59
  },
18
60
  schema: [],
19
61
  },
@@ -39,6 +81,7 @@ exports.noUnknownCssProperties = {
39
81
  },
40
82
  CallExpression(node) {
41
83
  let isCssProperties = false;
84
+ let isPositionTry = false;
42
85
  let isCreate = false;
43
86
  if (node.callee.type === 'MemberExpression') {
44
87
  if (node.callee.object.type === 'Identifier' &&
@@ -48,8 +91,10 @@ exports.noUnknownCssProperties = {
48
91
  : null;
49
92
  if (propertyName === 'create' ||
50
93
  propertyName === 'keyframes' ||
51
- propertyName === 'viewTransition') {
94
+ propertyName === 'viewTransition' ||
95
+ propertyName === 'positionTry') {
52
96
  isCssProperties = true;
97
+ isPositionTry = propertyName === 'positionTry';
53
98
  isCreate = propertyName === 'create';
54
99
  }
55
100
  }
@@ -58,14 +103,20 @@ exports.noUnknownCssProperties = {
58
103
  const alias = plumeriaAliases[node.callee.name];
59
104
  if (alias === 'create' ||
60
105
  alias === 'keyframes' ||
61
- alias === 'viewTransition') {
106
+ alias === 'viewTransition' ||
107
+ alias === 'positionTry') {
62
108
  isCssProperties = true;
109
+ isPositionTry = alias === 'positionTry';
63
110
  isCreate = alias === 'create';
64
111
  }
65
112
  }
66
113
  if (isCssProperties) {
67
114
  node.arguments.forEach((arg) => {
68
115
  if (arg.type === 'ObjectExpression') {
116
+ if (isPositionTry) {
117
+ checkPositionTry(arg);
118
+ return;
119
+ }
69
120
  arg.properties.forEach((prop) => {
70
121
  if (prop.type !== 'Property')
71
122
  return;
@@ -78,6 +129,38 @@ exports.noUnknownCssProperties = {
78
129
  }
79
130
  },
80
131
  };
132
+ function checkPositionTry(node) {
133
+ node.properties.forEach((prop) => {
134
+ if (prop.type !== 'Property')
135
+ return;
136
+ let keyName;
137
+ if (!prop.computed) {
138
+ keyName =
139
+ prop.key.type === 'Identifier'
140
+ ? prop.key.name
141
+ : String(prop.key.value);
142
+ }
143
+ else if (prop.key.type === 'Literal' &&
144
+ typeof prop.key.value === 'string') {
145
+ keyName = prop.key.value;
146
+ }
147
+ else {
148
+ return;
149
+ }
150
+ const kebabName = (0, zss_engine_1.camelToKebabCase)(keyName);
151
+ if (positionTryProperties.has(kebabName))
152
+ return;
153
+ context.report({
154
+ node: prop.key,
155
+ messageId: /^[:[@]|^--/.test(keyName) || knownProperties.has(kebabName)
156
+ ? 'positionTryProperty'
157
+ : 'unknownProperty',
158
+ data: {
159
+ name: keyName,
160
+ },
161
+ });
162
+ });
163
+ }
81
164
  function checkStyleObject(node, isCreate) {
82
165
  node.properties.forEach((prop) => {
83
166
  if (prop.type === 'Property') {
@@ -188,6 +188,7 @@ exports.sortProperties = {
188
188
  },
189
189
  CallExpression(node) {
190
190
  let isCssProperties = false;
191
+ let isPositionTry = false;
191
192
  if (node.callee.type === 'MemberExpression') {
192
193
  if (node.callee.object.type === 'Identifier' &&
193
194
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -196,8 +197,10 @@ exports.sortProperties = {
196
197
  : null;
197
198
  if (propertyName === 'create' ||
198
199
  propertyName === 'keyframes' ||
199
- propertyName === 'viewTransition') {
200
+ propertyName === 'viewTransition' ||
201
+ propertyName === 'positionTry') {
200
202
  isCssProperties = true;
203
+ isPositionTry = propertyName === 'positionTry';
201
204
  }
202
205
  }
203
206
  }
@@ -205,13 +208,19 @@ exports.sortProperties = {
205
208
  const alias = plumeriaAliases[node.callee.name];
206
209
  if (alias === 'create' ||
207
210
  alias === 'keyframes' ||
208
- alias === 'viewTransition') {
211
+ alias === 'viewTransition' ||
212
+ alias === 'positionTry') {
209
213
  isCssProperties = true;
214
+ isPositionTry = alias === 'positionTry';
210
215
  }
211
216
  }
212
217
  if (isCssProperties) {
213
218
  node.arguments.forEach((arg) => {
214
219
  if (arg.type === 'ObjectExpression') {
220
+ if (isPositionTry) {
221
+ checkStyleObject(arg);
222
+ return;
223
+ }
215
224
  arg.properties.forEach((prop) => {
216
225
  if (prop.type !== 'Property')
217
226
  return;
@@ -4,14 +4,10 @@ exports.validateAtRules = void 0;
4
4
  exports.isValidAtRule = isValidAtRule;
5
5
  const styleObject_1 = require("../util/styleObject");
6
6
  const staticValue_1 = require("../util/staticValue");
7
- const VALID_AT_RULES = [
8
- '@media',
9
- '@container',
10
- '@supports',
11
- '@layer',
12
- '@scope',
13
- ];
7
+ const VALID_AT_RULES = ['@media', '@container', '@supports'];
14
8
  function isValidAtRule(rule) {
9
+ if (rule === '@starting-style')
10
+ return true;
15
11
  return VALID_AT_RULES.some((keyword) => {
16
12
  if (!rule.startsWith(keyword))
17
13
  return false;
@@ -899,34 +899,35 @@ function getFoldedValues(key) {
899
899
  foldedValueCache.set(key, folded);
900
900
  return folded;
901
901
  }
902
+ const transformFunctionString = [
903
+ `matrix\\(${mathContentsString}\\)`,
904
+ `matrix3d\\(${mathContentsString}\\)`,
905
+ `perspective\\(${mathContentsString}\\)`,
906
+ `rotate\\(${mathContentsString}\\)`,
907
+ `rotate3d\\(${mathContentsString}\\)`,
908
+ `rotateX\\(${mathContentsString}\\)`,
909
+ `rotateY\\(${mathContentsString}\\)`,
910
+ `rotateZ\\(${mathContentsString}\\)`,
911
+ `scale\\(${mathContentsString}\\)`,
912
+ `scale3d\\(${mathContentsString}\\)`,
913
+ `scaleX\\(${mathContentsString}\\)`,
914
+ `scaleY\\(${mathContentsString}\\)`,
915
+ `scaleZ\\(${mathContentsString}\\)`,
916
+ `skew\\(${mathContentsString}\\)`,
917
+ `skewX\\(${mathContentsString}\\)`,
918
+ `skewY\\(${mathContentsString}\\)`,
919
+ `translate\\(${mathContentsString}\\)`,
920
+ `translate3d\\(${mathContentsString}\\)`,
921
+ `translateX\\(${mathContentsString}\\)`,
922
+ `translateY\\(${mathContentsString}\\)`,
923
+ `translateZ\\(${mathContentsString}\\)`,
924
+ ].join('|');
902
925
  function getValidator(key) {
903
926
  if (validatorCache.has(key))
904
927
  return validatorCache.get(key);
905
928
  let validator = null;
906
929
  const lvp = getLengthValuePattern(key);
907
- const getTransformFunctions = () => [
908
- `matrix\\(${mathContentsString}\\)`,
909
- `matrix3d\\(${mathContentsString}\\)`,
910
- `perspective\\(${mathContentsString}\\)`,
911
- `rotate\\(${mathContentsString}\\)`,
912
- `rotate3d\\(${mathContentsString}\\)`,
913
- `rotateX\\(${mathContentsString}\\)`,
914
- `rotateY\\(${mathContentsString}\\)`,
915
- `rotateZ\\(${mathContentsString}\\)`,
916
- `scale\\(${mathContentsString}\\)`,
917
- `scale3d\\(${mathContentsString}\\)`,
918
- `scaleX\\(${mathContentsString}\\)`,
919
- `scaleY\\(${mathContentsString}\\)`,
920
- `scaleZ\\(${mathContentsString}\\)`,
921
- `skew\\(${mathContentsString}\\)`,
922
- `skewX\\(${mathContentsString}\\)`,
923
- `skewY\\(${mathContentsString}\\)`,
924
- `translate\\(${mathContentsString}\\)`,
925
- `translate3d\\(${mathContentsString}\\)`,
926
- `translateX\\(${mathContentsString}\\)`,
927
- `translateY\\(${mathContentsString}\\)`,
928
- `translateZ\\(${mathContentsString}\\)`,
929
- ].join('|');
930
+ const getTransformFunctions = () => transformFunctionString;
930
931
  const getGeometryBaseSet = () => 'content-box|padding-box|border-box|fill-box|stroke-box|view-box';
931
932
  if (['translate'].includes(key)) {
932
933
  const r = new RegExp(`^(${lvp}|${percentagePattern})(\\s(${lvp}|${percentagePattern}))?(\\s(${lvp}))?$`);
@@ -1848,6 +1849,109 @@ function getValidator(key) {
1848
1849
  validatorCache.set(key, validator);
1849
1850
  return validator;
1850
1851
  }
1852
+ const contextDependentUnits = [
1853
+ 'cap',
1854
+ 'ch',
1855
+ 'em',
1856
+ 'ex',
1857
+ 'ic',
1858
+ 'lh',
1859
+ 'rcap',
1860
+ 'rch',
1861
+ 'rem',
1862
+ 'rex',
1863
+ 'ric',
1864
+ 'rlh',
1865
+ 'cqw',
1866
+ 'cqh',
1867
+ 'cqi',
1868
+ 'cqb',
1869
+ 'cqmin',
1870
+ 'cqmax',
1871
+ ];
1872
+ const contextDependentRegex = new RegExp(`(?:var\\(|[\\d.](?:${contextDependentUnits.join('|')})(?![a-zA-Z0-9_-]))`, 'i');
1873
+ const quotedStringRegex = new RegExp(stringString, 'g');
1874
+ const timeString = `${pureNumber}(?:ms|s)`;
1875
+ const resolutionString = `${pureNumber}(?:dpi|dpcm|dppx|x)`;
1876
+ const customIdentRegex = /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/;
1877
+ const exactly = (pattern) => {
1878
+ const r = new RegExp(`^(?:${pattern})$`);
1879
+ return (v) => r.test(v);
1880
+ };
1881
+ const varTypeValidators = {
1882
+ '<length>': exactly(`${lengthPattern}|${calcString}`),
1883
+ '<number>': exactly(`${pureNumber}|${calcString}`),
1884
+ '<integer>': exactly(`${pureInteger}|${calcString}`),
1885
+ '<percentage>': exactly(`${percentagePattern}|${calcString}`),
1886
+ '<length-percentage>': exactly(`${lengthPattern}|${percentagePattern}|${calcString}`),
1887
+ '<angle>': exactly(`${pureAngle}|${calcString}`),
1888
+ '<time>': exactly(`${timeString}|${calcString}`),
1889
+ '<resolution>': exactly(`${resolutionString}|${calcString}`),
1890
+ '<color>': isColor,
1891
+ '<image>': exactly(`${urlString}|${gradientString}|${imageSetString}`),
1892
+ '<url>': exactly(urlString),
1893
+ '<string>': exactly(stringString),
1894
+ '<custom-ident>': (v) => customIdentRegex.test(v) &&
1895
+ !globalValues.includes(v.toLowerCase()) &&
1896
+ v.toLowerCase() !== 'default',
1897
+ '<transform-function>': exactly(transformFunctionString),
1898
+ '<transform-list>': exactly(`(?:${transformFunctionString})(?:\\s+(?:${transformFunctionString}))*`),
1899
+ };
1900
+ function splitTopLevelSpaces(value) {
1901
+ const parts = [];
1902
+ let depth = 0;
1903
+ let current = '';
1904
+ for (const char of value) {
1905
+ if (char === '(')
1906
+ depth++;
1907
+ else if (char === ')')
1908
+ depth--;
1909
+ if (depth === 0 && /\s/.test(char)) {
1910
+ if (current)
1911
+ parts.push(current);
1912
+ current = '';
1913
+ }
1914
+ else {
1915
+ current += char;
1916
+ }
1917
+ }
1918
+ if (current)
1919
+ parts.push(current);
1920
+ return parts;
1921
+ }
1922
+ function matchesSyntaxComponent(component, value) {
1923
+ const multiplier = /[+#]$/.test(component) ? component.slice(-1) : '';
1924
+ const name = multiplier ? component.slice(0, -1) : component;
1925
+ if (!name.startsWith('<'))
1926
+ return multiplier ? null : value === name;
1927
+ const validator = varTypeValidators[name];
1928
+ if (!validator)
1929
+ return null;
1930
+ if (!multiplier)
1931
+ return validator(value);
1932
+ const items = multiplier === '+'
1933
+ ? splitTopLevelSpaces(value)
1934
+ : splitTopLevelCommas(value);
1935
+ return items.every((item) => item !== '' && validator(item));
1936
+ }
1937
+ function checkInitialValue(syntax, value) {
1938
+ const trimmedSyntax = syntax.trim();
1939
+ if (trimmedSyntax === '*')
1940
+ return null;
1941
+ if (contextDependentRegex.test(value.replace(quotedStringRegex, ''))) {
1942
+ return 'varTypeDependent';
1943
+ }
1944
+ const trimmedValue = value.trim();
1945
+ let unknown = false;
1946
+ for (const component of trimmedSyntax.split('|')) {
1947
+ const result = matchesSyntaxComponent(component.trim(), trimmedValue);
1948
+ if (result)
1949
+ return null;
1950
+ if (result === null)
1951
+ unknown = true;
1952
+ }
1953
+ return unknown ? null : 'varTypeMismatch';
1954
+ }
1851
1955
  exports.validateValues = {
1852
1956
  meta: {
1853
1957
  type: 'problem',
@@ -1858,6 +1962,8 @@ exports.validateValues = {
1858
1962
  validateValue: "'{{key}}' has an invalid value '{{value}}'. Valid values: {{validValues}}",
1859
1963
  invalidPrimitive: "'{{key}}' cannot be assigned a {{type}} value ({{value}}). CSS properties require string or number values.",
1860
1964
  invalidNumber: "'{{key}}' does not accept numeric values. Expected a string value.",
1965
+ varTypeMismatch: "css.varType initialValue '{{value}}' does not match the syntax '{{syntax}}'.",
1966
+ varTypeDependent: "css.varType initialValue '{{value}}' depends on the font, a container or var(), so the browser drops the @property. Use a value that does not depend on where it is used.",
1861
1967
  },
1862
1968
  schema: [],
1863
1969
  },
@@ -1947,6 +2053,43 @@ exports.validateValues = {
1947
2053
  }
1948
2054
  });
1949
2055
  }
2056
+ function checkVarType(arg) {
2057
+ if (!arg || arg.type !== 'ObjectExpression')
2058
+ return;
2059
+ let syntax = '*';
2060
+ let initialValue = null;
2061
+ for (const prop of arg.properties) {
2062
+ if (prop.type !== 'Property' ||
2063
+ prop.computed ||
2064
+ prop.key.type !== 'Identifier') {
2065
+ continue;
2066
+ }
2067
+ if (prop.key.name === 'syntax') {
2068
+ if (prop.value.type !== 'Literal')
2069
+ return;
2070
+ if (typeof prop.value.value !== 'string')
2071
+ return;
2072
+ syntax = prop.value.value;
2073
+ }
2074
+ else if (prop.key.name === 'initialValue') {
2075
+ initialValue = prop.value;
2076
+ }
2077
+ }
2078
+ if (!initialValue || initialValue.type !== 'Literal')
2079
+ return;
2080
+ const raw = initialValue.value;
2081
+ if (typeof raw !== 'string' && typeof raw !== 'number')
2082
+ return;
2083
+ const value = String(raw);
2084
+ const messageId = checkInitialValue(syntax, value);
2085
+ if (messageId) {
2086
+ context.report({
2087
+ node: initialValue,
2088
+ messageId,
2089
+ data: { value, syntax },
2090
+ });
2091
+ }
2092
+ }
1950
2093
  return {
1951
2094
  ImportDeclaration(node) {
1952
2095
  if (node.source.value === '@plumeria/core') {
@@ -1966,7 +2109,21 @@ exports.validateValues = {
1966
2109
  }
1967
2110
  },
1968
2111
  CallExpression(node) {
2112
+ const calleeName = node.callee.type === 'MemberExpression'
2113
+ ? node.callee.object.type === 'Identifier' &&
2114
+ plumeriaAliases[node.callee.object.name] === 'NAMESPACE' &&
2115
+ node.callee.property.type === 'Identifier'
2116
+ ? node.callee.property.name
2117
+ : null
2118
+ : node.callee.type === 'Identifier'
2119
+ ? plumeriaAliases[node.callee.name]
2120
+ : null;
2121
+ if (calleeName === 'varType') {
2122
+ checkVarType(node.arguments[0]);
2123
+ return;
2124
+ }
1969
2125
  let isCssProperties = false;
2126
+ let isPositionTry = false;
1970
2127
  if (node.callee.type === 'MemberExpression') {
1971
2128
  if (node.callee.object.type === 'Identifier' &&
1972
2129
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -1975,8 +2132,10 @@ exports.validateValues = {
1975
2132
  : null;
1976
2133
  if (propertyName === 'create' ||
1977
2134
  propertyName === 'keyframes' ||
1978
- propertyName === 'viewTransition') {
2135
+ propertyName === 'viewTransition' ||
2136
+ propertyName === 'positionTry') {
1979
2137
  isCssProperties = true;
2138
+ isPositionTry = propertyName === 'positionTry';
1980
2139
  }
1981
2140
  }
1982
2141
  }
@@ -1984,14 +2143,20 @@ exports.validateValues = {
1984
2143
  const alias = plumeriaAliases[node.callee.name];
1985
2144
  if (alias === 'create' ||
1986
2145
  alias === 'keyframes' ||
1987
- alias === 'viewTransition') {
2146
+ alias === 'viewTransition' ||
2147
+ alias === 'positionTry') {
1988
2148
  isCssProperties = true;
2149
+ isPositionTry = alias === 'positionTry';
1989
2150
  }
1990
2151
  }
1991
2152
  if (!isCssProperties)
1992
2153
  return;
1993
2154
  node.arguments.forEach((arg) => {
1994
2155
  if (arg.type === 'ObjectExpression') {
2156
+ if (isPositionTry) {
2157
+ checkStyleObject(arg);
2158
+ return;
2159
+ }
1995
2160
  arg.properties.forEach((prop) => {
1996
2161
  if (prop.type !== 'Property')
1997
2162
  return;
@@ -11,6 +11,14 @@ exports.propertyGroups = [
11
11
  {
12
12
  properties: [
13
13
  'position',
14
+ 'positionAnchor',
15
+ 'positionArea',
16
+ 'positionTry',
17
+ 'positionTryOrder',
18
+ 'positionTryFallbacks',
19
+ 'positionVisibility',
20
+ 'anchorName',
21
+ 'anchorScope',
14
22
  'inset',
15
23
  'insetBlock',
16
24
  'insetBlockStart',
@@ -53,6 +53,7 @@ const createSpellingRule = (reject) => ({
53
53
  },
54
54
  CallExpression(node) {
55
55
  let isCssProperties = false;
56
+ let isPositionTry = false;
56
57
  if (node.callee.type === 'MemberExpression') {
57
58
  if (node.callee.object.type === 'Identifier' &&
58
59
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -61,8 +62,10 @@ const createSpellingRule = (reject) => ({
61
62
  : null;
62
63
  if (propertyName === 'create' ||
63
64
  propertyName === 'keyframes' ||
64
- propertyName === 'viewTransition') {
65
+ propertyName === 'viewTransition' ||
66
+ propertyName === 'positionTry') {
65
67
  isCssProperties = true;
68
+ isPositionTry = propertyName === 'positionTry';
66
69
  }
67
70
  }
68
71
  }
@@ -70,13 +73,19 @@ const createSpellingRule = (reject) => ({
70
73
  const aliasName = plumeriaAliases[node.callee.name];
71
74
  if (aliasName === 'create' ||
72
75
  aliasName === 'keyframes' ||
73
- aliasName === 'viewTransition') {
76
+ aliasName === 'viewTransition' ||
77
+ aliasName === 'positionTry') {
74
78
  isCssProperties = true;
79
+ isPositionTry = aliasName === 'positionTry';
75
80
  }
76
81
  }
77
82
  if (isCssProperties) {
78
83
  node.arguments.forEach((arg) => {
79
84
  if (arg.type === 'ObjectExpression') {
85
+ if (isPositionTry) {
86
+ checkStyleObject(arg);
87
+ return;
88
+ }
80
89
  arg.properties.forEach((prop) => {
81
90
  if (prop.type !== 'Property')
82
91
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "19.14.4",
3
+ "version": "20.0.0",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -58,8 +58,8 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "oxlint": "1.87.0",
61
- "zss-engine": "2.10.0",
62
- "@plumeria/compiler": "19.14.4"
61
+ "zss-engine": "3.0.1",
62
+ "@plumeria/compiler": "20.0.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "rimraf dist && pnpm cjs",