@bamboocss/eslint-plugin 1.41.1 → 1.43.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/dist/index.cjs CHANGED
@@ -8,7 +8,7 @@ require("@typescript-eslint/utils/ts-eslint");
8
8
  let _bamboocss_shared = require("@bamboocss/shared");
9
9
  //#region package.json
10
10
  var name = "@bamboocss/eslint-plugin";
11
- var version = "1.41.1";
11
+ var version = "1.43.0";
12
12
  //#endregion
13
13
  //#region src/utils/index.ts
14
14
  const createRule = _typescript_eslint_utils.ESLintUtils.RuleCreator((name) => `https://github.com/gajus/bamboocss/blob/main/packages/eslint-plugin/docs/rules/${name}.md`);
@@ -252,6 +252,22 @@ const getInvalidTokens = (value, context) => {
252
252
  invalidTokensCache.set(value, invalidTokens);
253
253
  return invalidTokens;
254
254
  };
255
+ const unresolvedValueCache = /* @__PURE__ */ new Map();
256
+ /**
257
+ * What the build would say about this property and value, or nothing if it is fine.
258
+ *
259
+ * Asked of the resolver rather than reimplemented, so the squiggle and the build agree by
260
+ * construction. `getInvalidTokens` above answers a narrower question — is this dotted path a
261
+ * token — and cannot see `color: 'mutedd'` or tell `top: 'navH'` from `animationName: 'fadeIn'`,
262
+ * because neither is decidable without the property and its CSS grammar.
263
+ */
264
+ const getUnresolvedValueMessage = (property, value, context) => {
265
+ const id = `${property}:${value}`;
266
+ if (unresolvedValueCache.has(id)) return unresolvedValueCache.get(id);
267
+ const message = syncAction("explainUnresolvedValue", getSyncOptions(context), property, value);
268
+ unresolvedValueCache.set(id, message);
269
+ return message;
270
+ };
255
271
  const deprecatedTokensCache = /* @__PURE__ */ new Map();
256
272
  const getDeprecatedTokens = (property, value, context) => {
257
273
  const propertyCategory = syncAction("getPropCategory", getSyncOptions(context), property);
@@ -1058,25 +1074,45 @@ const rule$12 = createRule({
1058
1074
  node
1059
1075
  });
1060
1076
  };
1061
- const handleLiteralOrTemplate = (node) => {
1077
+ /**
1078
+ * With the property in hand, ask the resolver the whole question.
1079
+ *
1080
+ * `getInvalidTokens` can only answer "is this dotted path a token", because that is all a
1081
+ * value tells you on its own. The property is what makes `color: 'mutedd'` decidable at all,
1082
+ * and what separates `top: 'navH'` from `animationName: 'fadeIn'` — one is a typo and the
1083
+ * other is a `<custom-ident>` the grammar asks for.
1084
+ */
1085
+ const reportUnresolved = (node, property, value) => {
1086
+ if (!value) return;
1087
+ const message = getUnresolvedValueMessage(property, value, context);
1088
+ if (message) context.report({
1089
+ data: { message },
1090
+ messageId: "unresolvedValue",
1091
+ node
1092
+ });
1093
+ };
1094
+ const handleValue = (node, property) => {
1062
1095
  if (!node) return;
1063
1096
  if (isLiteral(node)) {
1064
1097
  const value = node.value?.toString();
1065
1098
  sendReport(node, value);
1099
+ if (property) reportUnresolved(node, property, value);
1066
1100
  } else if (isTemplateLiteral(node) && node.expressions.length === 0) {
1067
1101
  const value = node.quasis[0].value.raw;
1068
1102
  sendReport(node.quasis[0], value);
1103
+ if (property) reportUnresolved(node.quasis[0], property, value);
1069
1104
  }
1070
1105
  };
1071
1106
  return {
1072
1107
  JSXAttribute(node) {
1073
1108
  if (!node.value || !isBambooProp(node, context)) return;
1074
- if (isLiteral(node.value)) handleLiteralOrTemplate(node.value);
1075
- else if (isJSXExpressionContainer(node.value)) handleLiteralOrTemplate(node.value.expression);
1109
+ const property = isJSXIdentifier(node.name) ? node.name.name : void 0;
1110
+ if (isLiteral(node.value)) handleValue(node.value, property);
1111
+ else if (isJSXExpressionContainer(node.value)) handleValue(node.value.expression, property);
1076
1112
  },
1077
1113
  Property(node) {
1078
1114
  if (!isIdentifier(node.key) || !(0, _typescript_eslint_utils_ast_utils.isNodeOfTypes)([_typescript_eslint_utils.AST_NODE_TYPES.Literal, _typescript_eslint_utils.AST_NODE_TYPES.TemplateLiteral])(node.value) || !isBambooAttribute(node, context) || isRecipeVariant(node, context)) return;
1079
- handleLiteralOrTemplate(node.value);
1115
+ handleValue(node.value, node.key.name);
1080
1116
  },
1081
1117
  TaggedTemplateExpression(node) {
1082
1118
  if (!getTaggedTemplateCaller(node)) return;
@@ -1114,7 +1150,10 @@ const rule$12 = createRule({
1114
1150
  defaultOptions: [],
1115
1151
  meta: {
1116
1152
  docs: { description: "Disallow the use of invalid token paths within token function syntax." },
1117
- messages: { noInvalidTokenPaths: "`{{token}}` is an invalid token path." },
1153
+ messages: {
1154
+ noInvalidTokenPaths: "`{{token}}` is an invalid token path.",
1155
+ unresolvedValue: "{{message}}"
1156
+ },
1118
1157
  schema: [],
1119
1158
  type: "problem"
1120
1159
  },
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import "@typescript-eslint/utils/ts-eslint";
8
8
  import { getArbitraryValue, parseFallbackValue } from "@bamboocss/shared";
9
9
  //#region package.json
10
10
  var name = "@bamboocss/eslint-plugin";
11
- var version = "1.41.1";
11
+ var version = "1.43.0";
12
12
  //#endregion
13
13
  //#region src/utils/index.ts
14
14
  const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/gajus/bamboocss/blob/main/packages/eslint-plugin/docs/rules/${name}.md`);
@@ -252,6 +252,22 @@ const getInvalidTokens = (value, context) => {
252
252
  invalidTokensCache.set(value, invalidTokens);
253
253
  return invalidTokens;
254
254
  };
255
+ const unresolvedValueCache = /* @__PURE__ */ new Map();
256
+ /**
257
+ * What the build would say about this property and value, or nothing if it is fine.
258
+ *
259
+ * Asked of the resolver rather than reimplemented, so the squiggle and the build agree by
260
+ * construction. `getInvalidTokens` above answers a narrower question — is this dotted path a
261
+ * token — and cannot see `color: 'mutedd'` or tell `top: 'navH'` from `animationName: 'fadeIn'`,
262
+ * because neither is decidable without the property and its CSS grammar.
263
+ */
264
+ const getUnresolvedValueMessage = (property, value, context) => {
265
+ const id = `${property}:${value}`;
266
+ if (unresolvedValueCache.has(id)) return unresolvedValueCache.get(id);
267
+ const message = syncAction("explainUnresolvedValue", getSyncOptions(context), property, value);
268
+ unresolvedValueCache.set(id, message);
269
+ return message;
270
+ };
255
271
  const deprecatedTokensCache = /* @__PURE__ */ new Map();
256
272
  const getDeprecatedTokens = (property, value, context) => {
257
273
  const propertyCategory = syncAction("getPropCategory", getSyncOptions(context), property);
@@ -1058,25 +1074,45 @@ const rule$12 = createRule({
1058
1074
  node
1059
1075
  });
1060
1076
  };
1061
- const handleLiteralOrTemplate = (node) => {
1077
+ /**
1078
+ * With the property in hand, ask the resolver the whole question.
1079
+ *
1080
+ * `getInvalidTokens` can only answer "is this dotted path a token", because that is all a
1081
+ * value tells you on its own. The property is what makes `color: 'mutedd'` decidable at all,
1082
+ * and what separates `top: 'navH'` from `animationName: 'fadeIn'` — one is a typo and the
1083
+ * other is a `<custom-ident>` the grammar asks for.
1084
+ */
1085
+ const reportUnresolved = (node, property, value) => {
1086
+ if (!value) return;
1087
+ const message = getUnresolvedValueMessage(property, value, context);
1088
+ if (message) context.report({
1089
+ data: { message },
1090
+ messageId: "unresolvedValue",
1091
+ node
1092
+ });
1093
+ };
1094
+ const handleValue = (node, property) => {
1062
1095
  if (!node) return;
1063
1096
  if (isLiteral(node)) {
1064
1097
  const value = node.value?.toString();
1065
1098
  sendReport(node, value);
1099
+ if (property) reportUnresolved(node, property, value);
1066
1100
  } else if (isTemplateLiteral(node) && node.expressions.length === 0) {
1067
1101
  const value = node.quasis[0].value.raw;
1068
1102
  sendReport(node.quasis[0], value);
1103
+ if (property) reportUnresolved(node.quasis[0], property, value);
1069
1104
  }
1070
1105
  };
1071
1106
  return {
1072
1107
  JSXAttribute(node) {
1073
1108
  if (!node.value || !isBambooProp(node, context)) return;
1074
- if (isLiteral(node.value)) handleLiteralOrTemplate(node.value);
1075
- else if (isJSXExpressionContainer(node.value)) handleLiteralOrTemplate(node.value.expression);
1109
+ const property = isJSXIdentifier(node.name) ? node.name.name : void 0;
1110
+ if (isLiteral(node.value)) handleValue(node.value, property);
1111
+ else if (isJSXExpressionContainer(node.value)) handleValue(node.value.expression, property);
1076
1112
  },
1077
1113
  Property(node) {
1078
1114
  if (!isIdentifier(node.key) || !isNodeOfTypes([AST_NODE_TYPES.Literal, AST_NODE_TYPES.TemplateLiteral])(node.value) || !isBambooAttribute(node, context) || isRecipeVariant(node, context)) return;
1079
- handleLiteralOrTemplate(node.value);
1115
+ handleValue(node.value, node.key.name);
1080
1116
  },
1081
1117
  TaggedTemplateExpression(node) {
1082
1118
  if (!getTaggedTemplateCaller(node)) return;
@@ -1114,7 +1150,10 @@ const rule$12 = createRule({
1114
1150
  defaultOptions: [],
1115
1151
  meta: {
1116
1152
  docs: { description: "Disallow the use of invalid token paths within token function syntax." },
1117
- messages: { noInvalidTokenPaths: "`{{token}}` is an invalid token path." },
1153
+ messages: {
1154
+ noInvalidTokenPaths: "`{{token}}` is an invalid token path.",
1155
+ unresolvedValue: "{{message}}"
1156
+ },
1118
1157
  schema: [],
1119
1158
  type: "problem"
1120
1159
  },
@@ -63,6 +63,21 @@ const filterDeprecatedTokens = (context, tokens) => {
63
63
  const filterInvalidTokens = (context, paths) => {
64
64
  return paths.filter((path$2) => !context.utility.tokens.view.get(path$2));
65
65
  };
66
+ /**
67
+ * The build's own verdict on one property/value pair, for the editor.
68
+ *
69
+ * The same predicate and the same sentence the build prints — not a second implementation of
70
+ * either. The type layer used to give this feedback by narrowing every generated prop type, at
71
+ * a measured 19% of the types and 28% of the instantiations in a real project, and it could
72
+ * only ever say "not assignable"; the resolver knows the value is a `sizes` token used on a
73
+ * property that reads `spacing`, and knowing that is the whole point.
74
+ */
75
+ const explainUnresolvedValue = (context, property, value) => {
76
+ const { utility } = context;
77
+ if (!utility.isUnresolvedTokenValue(property, value)) return void 0;
78
+ const key = utility.resolveShorthand(property);
79
+ return utility.explainUnresolvedToken(utility.unresolvedTokenRef(key, utility.bareTokenPath(key, value)));
80
+ };
66
81
  const getPropertyCategory = (context, _attribute) => {
67
82
  const attribute = resolveLongHand(context, _attribute) || _attribute;
68
83
  const attributeConfig = context.utility.config[attribute];
@@ -82,6 +97,7 @@ async function runAsync(action, options, ...args) {
82
97
  switch (action) {
83
98
  case "filterDeprecatedTokens": return filterDeprecatedTokens(context, ...args);
84
99
  case "filterImports": return filterImports(context, ...args);
100
+ case "explainUnresolvedValue": return explainUnresolvedValue(context, ...args);
85
101
  case "filterInvalidTokens": return filterInvalidTokens(context, ...args);
86
102
  case "getPropCategory": return getPropertyCategory(context, ...args);
87
103
  case "isColorAttribute": return isColorAttribute(context, ...args);
@@ -22,6 +22,7 @@ type MatchImportResult = {
22
22
  name: string;
23
23
  };
24
24
  declare function run(action: 'filterInvalidTokens', options: Options, paths: string[]): string[];
25
+ declare function run(action: 'explainUnresolvedValue', options: Options, property: string, value: string): string | undefined;
25
26
  declare function run(action: 'isColorToken', options: Options, value: string): boolean;
26
27
  declare function run(action: 'isColorAttribute', options: Options, attribute: string): boolean;
27
28
  declare function run(action: 'isValidFile', options: Options): boolean;
@@ -34,6 +35,7 @@ declare function run(action: 'filterImports', options: Options, imports: MatchIm
34
35
  declare function run(action: 'getPropCategory', options: Options, property: string): string;
35
36
  declare function run(action: 'filterDeprecatedTokens', options: Options, tokens: DeprecatedToken[]): DeprecatedToken[];
36
37
  declare function runAsync(action: 'filterInvalidTokens', options: Options, paths: string[]): Promise<string[]>;
38
+ declare function runAsync(action: 'explainUnresolvedValue', options: Options, property: string, value: string): Promise<string | undefined>;
37
39
  declare function runAsync(action: 'isColorToken', options: Options, value: string): Promise<boolean>;
38
40
  declare function runAsync(action: 'isColorAttribute', options: Options, attribute: string): Promise<boolean>;
39
41
  declare function runAsync(action: 'isValidFile', options: Options, fileName: string): Promise<string>;
@@ -22,6 +22,7 @@ type MatchImportResult = {
22
22
  name: string;
23
23
  };
24
24
  declare function run(action: 'filterInvalidTokens', options: Options, paths: string[]): string[];
25
+ declare function run(action: 'explainUnresolvedValue', options: Options, property: string, value: string): string | undefined;
25
26
  declare function run(action: 'isColorToken', options: Options, value: string): boolean;
26
27
  declare function run(action: 'isColorAttribute', options: Options, attribute: string): boolean;
27
28
  declare function run(action: 'isValidFile', options: Options): boolean;
@@ -34,6 +35,7 @@ declare function run(action: 'filterImports', options: Options, imports: MatchIm
34
35
  declare function run(action: 'getPropCategory', options: Options, property: string): string;
35
36
  declare function run(action: 'filterDeprecatedTokens', options: Options, tokens: DeprecatedToken[]): DeprecatedToken[];
36
37
  declare function runAsync(action: 'filterInvalidTokens', options: Options, paths: string[]): Promise<string[]>;
38
+ declare function runAsync(action: 'explainUnresolvedValue', options: Options, property: string, value: string): Promise<string | undefined>;
37
39
  declare function runAsync(action: 'isColorToken', options: Options, value: string): Promise<boolean>;
38
40
  declare function runAsync(action: 'isColorAttribute', options: Options, attribute: string): Promise<boolean>;
39
41
  declare function runAsync(action: 'isValidFile', options: Options, fileName: string): Promise<string>;
@@ -38,6 +38,21 @@ const filterDeprecatedTokens = (context, tokens) => {
38
38
  const filterInvalidTokens = (context, paths) => {
39
39
  return paths.filter((path) => !context.utility.tokens.view.get(path));
40
40
  };
41
+ /**
42
+ * The build's own verdict on one property/value pair, for the editor.
43
+ *
44
+ * The same predicate and the same sentence the build prints — not a second implementation of
45
+ * either. The type layer used to give this feedback by narrowing every generated prop type, at
46
+ * a measured 19% of the types and 28% of the instantiations in a real project, and it could
47
+ * only ever say "not assignable"; the resolver knows the value is a `sizes` token used on a
48
+ * property that reads `spacing`, and knowing that is the whole point.
49
+ */
50
+ const explainUnresolvedValue = (context, property, value) => {
51
+ const { utility } = context;
52
+ if (!utility.isUnresolvedTokenValue(property, value)) return void 0;
53
+ const key = utility.resolveShorthand(property);
54
+ return utility.explainUnresolvedToken(utility.unresolvedTokenRef(key, utility.bareTokenPath(key, value)));
55
+ };
41
56
  const getPropertyCategory = (context, _attribute) => {
42
57
  const attribute = resolveLongHand(context, _attribute) || _attribute;
43
58
  const attributeConfig = context.utility.config[attribute];
@@ -57,6 +72,7 @@ async function runAsync(action, options, ...args) {
57
72
  switch (action) {
58
73
  case "filterDeprecatedTokens": return filterDeprecatedTokens(context, ...args);
59
74
  case "filterImports": return filterImports(context, ...args);
75
+ case "explainUnresolvedValue": return explainUnresolvedValue(context, ...args);
60
76
  case "filterInvalidTokens": return filterInvalidTokens(context, ...args);
61
77
  case "getPropCategory": return getPropertyCategory(context, ...args);
62
78
  case "isColorAttribute": return isColorAttribute(context, ...args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bamboocss/eslint-plugin",
3
- "version": "1.41.1",
3
+ "version": "1.43.0",
4
4
  "description": "ESLint plugin for Bamboo CSS",
5
5
  "homepage": "https://bamboocss.com",
6
6
  "license": "MIT",
@@ -36,9 +36,9 @@
36
36
  "@typescript-eslint/utils": "^8.21.0",
37
37
  "micromatch": "^4.0.8",
38
38
  "synckit": "^0.9.0",
39
- "@bamboocss/config": "1.41.1",
40
- "@bamboocss/generator": "1.41.1",
41
- "@bamboocss/shared": "1.41.1"
39
+ "@bamboocss/config": "1.43.0",
40
+ "@bamboocss/generator": "1.43.0",
41
+ "@bamboocss/shared": "1.43.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/micromatch": "^4.0.10",
@@ -46,10 +46,10 @@
46
46
  "eslint": "^9.35.0",
47
47
  "multiline-ts": "^4.0.1",
48
48
  "typescript": "^5.7.2",
49
- "@bamboocss/dev": "1.41.1",
50
- "@bamboocss/preset-bamboo": "1.41.1",
51
- "@bamboocss/preset-base": "1.41.1",
52
- "@bamboocss/types": "1.41.1"
49
+ "@bamboocss/dev": "1.43.0",
50
+ "@bamboocss/preset-bamboo": "1.43.0",
51
+ "@bamboocss/preset-base": "1.43.0",
52
+ "@bamboocss/types": "1.43.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "eslint": "*"