@atlaskit/eslint-plugin-design-system 10.17.3 → 10.18.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/rules/use-latest-xcss-syntax-typography/config/index.js +2 -1
  3. package/dist/cjs/rules/use-latest-xcss-syntax-typography/index.js +22 -5
  4. package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/common.js +5 -0
  5. package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/index.js +10 -3
  6. package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/{banned-property/index.js → restricted-property.js} +30 -10
  7. package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.js +50 -0
  8. package/dist/es2019/rules/use-latest-xcss-syntax-typography/config/index.js +2 -1
  9. package/dist/es2019/rules/use-latest-xcss-syntax-typography/index.js +19 -6
  10. package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/common.js +1 -0
  11. package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/index.js +2 -1
  12. package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/restricted-property.js +61 -0
  13. package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.js +44 -0
  14. package/dist/esm/rules/use-latest-xcss-syntax-typography/config/index.js +2 -1
  15. package/dist/esm/rules/use-latest-xcss-syntax-typography/index.js +23 -6
  16. package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/common.js +1 -0
  17. package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/index.js +2 -1
  18. package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/{banned-property/index.js → restricted-property.js} +29 -9
  19. package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.js +44 -0
  20. package/dist/types/rules/use-latest-xcss-syntax-typography/config/index.d.ts +3 -0
  21. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +6 -0
  22. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/index.d.ts +2 -1
  23. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/restricted-property.d.ts +6 -0
  24. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.d.ts +7 -0
  25. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/config/index.d.ts +3 -0
  26. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +6 -0
  27. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/index.d.ts +2 -1
  28. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/restricted-property.d.ts +6 -0
  29. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.d.ts +7 -0
  30. package/package.json +1 -5
  31. package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/banned-property/index.js +0 -40
  32. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/banned-property/index.d.ts +0 -7
  33. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/banned-property/index.d.ts +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 10.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#136089](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/136089)
8
+ [`d0e47b2e03130`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/d0e47b2e03130) -
9
+ Disallow wrapped tokens for typography properties in XCSS using the
10
+ `use-latest-xcss-syntax-typography` rule.
11
+
3
12
  ## 10.17.3
4
13
 
5
14
  ### Patch Changes
@@ -5,7 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getConfig = void 0;
7
7
  var defaults = {
8
- failSilently: false
8
+ failSilently: false,
9
+ patterns: ['restricted-property', 'wrapped-token-value']
9
10
  };
10
11
  var getConfig = exports.getConfig = function getConfig(overrides) {
11
12
  return Object.assign({}, defaults, overrides);
@@ -8,6 +8,7 @@ var _createRule = require("../utils/create-rule");
8
8
  var _errorBoundary = require("../utils/error-boundary");
9
9
  var _config = require("./config");
10
10
  var _linters = require("./linters");
11
+ var typescriptErrorMessage = 'There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.';
11
12
  var rule = (0, _createRule.createLintRule)({
12
13
  meta: {
13
14
  name: 'use-latest-xcss-syntax-typography',
@@ -20,20 +21,36 @@ var rule = (0, _createRule.createLintRule)({
20
21
  severity: 'warn'
21
22
  },
22
23
  messages: {
23
- noUnsafeTypographyProperties: "Don't set '{{ property }}' on xcss. They are unsafe as they allow invalid combinations of typography tokens. There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway."
24
+ noRestrictedTypographyProperties: "Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ".concat(typescriptErrorMessage),
25
+ noRestrictedTypographyPropertiesHeading: "Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ".concat(typescriptErrorMessage),
26
+ noWrappedTokenTypographyValues: "Don't wrap typography tokens in xcss. ".concat(typescriptErrorMessage)
24
27
  }
25
28
  },
26
29
  create: function create(context) {
27
30
  var config = (0, _config.getConfig)(context.options[0]);
28
31
  return (0, _errorBoundary.errorBoundary)({
29
32
  'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontSizeLineHeightFontWeightLetterSpacing(node) {
30
- return _linters.BannedProperty.lint(node, {
31
- context: context
33
+ return _linters.RestrictedProperty.lint(node, {
34
+ context: context,
35
+ config: config
32
36
  });
33
37
  },
34
38
  'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueFontSizeLineHeightFontWeightLetterSpacing(node) {
35
- return _linters.BannedProperty.lint(node, {
36
- context: context
39
+ return _linters.RestrictedProperty.lint(node, {
40
+ context: context,
41
+ config: config
42
+ });
43
+ },
44
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontFontFamilyFontWeight(node) {
45
+ return _linters.WrappedTokenValue.lint(node, {
46
+ context: context,
47
+ config: config
48
+ });
49
+ },
50
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueFontFontFamilyFontWeight(node) {
51
+ return _linters.WrappedTokenValue.lint(node, {
52
+ context: context,
53
+ config: config
37
54
  });
38
55
  }
39
56
  }, config);
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -3,10 +3,17 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- Object.defineProperty(exports, "BannedProperty", {
6
+ Object.defineProperty(exports, "RestrictedProperty", {
7
7
  enumerable: true,
8
8
  get: function get() {
9
- return _bannedProperty.BannedProperty;
9
+ return _restrictedProperty.RestrictedProperty;
10
10
  }
11
11
  });
12
- var _bannedProperty = require("./banned-property");
12
+ Object.defineProperty(exports, "WrappedTokenValue", {
13
+ enumerable: true,
14
+ get: function get() {
15
+ return _wrappedTokenValue.WrappedTokenValue;
16
+ }
17
+ });
18
+ var _restrictedProperty = require("./restricted-property");
19
+ var _wrappedTokenValue = require("./wrapped-token-value");
@@ -3,15 +3,19 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.BannedProperty = void 0;
6
+ exports.RestrictedProperty = void 0;
7
7
  var _eslintCodemodUtils = require("eslint-codemod-utils");
8
8
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9
9
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
10
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /* eslint-disable @repo/internal/react/require-jsdoc */
11
- var BannedProperty = exports.BannedProperty = {
11
+ var RestrictedProperty = exports.RestrictedProperty = {
12
12
  lint: function lint(node, _ref) {
13
- var context = _ref.context;
14
- if (BannedProperty._check(node)) {
13
+ var context = _ref.context,
14
+ config = _ref.config;
15
+ if (RestrictedProperty._check(node, {
16
+ context: context,
17
+ config: config
18
+ })) {
15
19
  var property = 'fontSize, lineHeight, fontWeight or letterSpacing';
16
20
  if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier')) {
17
21
  property = node.name;
@@ -20,14 +24,19 @@ var BannedProperty = exports.BannedProperty = {
20
24
  }
21
25
  context.report({
22
26
  node: node,
23
- messageId: 'noUnsafeTypographyProperties',
27
+ messageId: property === 'fontWeight' ? 'noRestrictedTypographyPropertiesHeading' : 'noRestrictedTypographyProperties',
24
28
  data: {
25
29
  property: property
26
30
  }
27
31
  });
28
32
  }
29
33
  },
30
- _check: function _check(node) {
34
+ _check: function _check(node, _ref2) {
35
+ var config = _ref2.config;
36
+ if (!config.patterns.includes('restricted-property')) {
37
+ return false;
38
+ }
39
+
31
40
  // Prevent font weight being used in combination with heading tokens
32
41
  if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') && node.name === 'fontWeight' || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') && node.value === 'fontWeight') {
33
42
  if ((0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent, 'ObjectExpression')) {
@@ -35,11 +44,22 @@ var BannedProperty = exports.BannedProperty = {
35
44
  _step;
36
45
  try {
37
46
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
47
+ var _property$value$value;
38
48
  var property = _step.value;
39
- if ((0, _eslintCodemodUtils.isNodeOfType)(property, 'Property') && (0, _eslintCodemodUtils.isNodeOfType)(property.value, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(property.value.callee, 'Identifier') && (0, _eslintCodemodUtils.isNodeOfType)(property.value.arguments[0], 'Literal')) {
40
- if (property.value.callee.name === 'token' && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.includes('font.heading')) {
41
- return true;
42
- }
49
+ // Only looking for heading token on `font` property
50
+ var isFontProperty = (0, _eslintCodemodUtils.isNodeOfType)(property, 'Property') && ((0, _eslintCodemodUtils.isNodeOfType)(property.key, 'Literal') && property.key.value === 'font' || (0, _eslintCodemodUtils.isNodeOfType)(property.key, 'Identifier') && property.key.name === 'font');
51
+ if (!isFontProperty) {
52
+ continue;
53
+ }
54
+
55
+ // Checking for heading token string, for example xcss({ font: 'font.heading.medium' })
56
+ if ((0, _eslintCodemodUtils.isNodeOfType)(property.value, 'Literal') && typeof property.value.value === 'string' && (_property$value$value = property.value.value) !== null && _property$value$value !== void 0 && _property$value$value.startsWith('font.heading')) {
57
+ return true;
58
+ }
59
+
60
+ // Checking for wrapped heading token, for example xcss({ font: token('font.heading.medium') })
61
+ if ((0, _eslintCodemodUtils.isNodeOfType)(property.value, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(property.value.callee, 'Identifier') && property.value.callee.name === 'token' && (0, _eslintCodemodUtils.isNodeOfType)(property.value.arguments[0], 'Literal') && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.startsWith('font.heading')) {
62
+ return true;
43
63
  }
44
64
  }
45
65
  } catch (err) {
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.WrappedTokenValue = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ /* eslint-disable @repo/internal/react/require-jsdoc */
9
+
10
+ var messageId = 'noWrappedTokenTypographyValues';
11
+ var WrappedTokenValue = exports.WrappedTokenValue = {
12
+ lint: function lint(node, _ref) {
13
+ var context = _ref.context,
14
+ config = _ref.config;
15
+ if (WrappedTokenValue._check(node, {
16
+ context: context,
17
+ config: config
18
+ })) {
19
+ context.report({
20
+ node: node,
21
+ messageId: messageId,
22
+ fix: WrappedTokenValue._fix(node)
23
+ });
24
+ }
25
+ },
26
+ _check: function _check(node, _ref2) {
27
+ var config = _ref2.config;
28
+ if (!config.patterns.includes('wrapped-token-value')) {
29
+ return false;
30
+ }
31
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'Property') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.value, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.value.callee, 'Identifier') && node.parent.value.callee.name === 'token' && node.parent.value.arguments.length >= 1) {
32
+ return true;
33
+ }
34
+ return false;
35
+ },
36
+ _fix: function _fix(node) {
37
+ return function (fixer) {
38
+ var wrappedTokenFix;
39
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'Property') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.value, 'CallExpression') && node.parent.value.arguments.length >= 1) {
40
+ var firstArg = node.parent.value.arguments[0];
41
+ if ((0, _eslintCodemodUtils.isNodeOfType)(firstArg, 'Literal') && typeof firstArg.value === 'string') {
42
+ wrappedTokenFix = fixer.replaceText(node.parent.value, "'".concat(firstArg.value, "'"));
43
+ }
44
+ }
45
+ return [wrappedTokenFix].filter(function (fix) {
46
+ return Boolean(fix);
47
+ }); // Some of the transformers can return arrays with undefined, so filter them out
48
+ };
49
+ }
50
+ };
@@ -1,5 +1,6 @@
1
1
  const defaults = {
2
- failSilently: false
2
+ failSilently: false,
3
+ patterns: ['restricted-property', 'wrapped-token-value']
3
4
  };
4
5
  export const getConfig = overrides => {
5
6
  return Object.assign({}, defaults, overrides);
@@ -1,7 +1,8 @@
1
1
  import { createLintRule } from '../utils/create-rule';
2
2
  import { errorBoundary } from '../utils/error-boundary';
3
3
  import { getConfig } from './config';
4
- import { BannedProperty } from './linters';
4
+ import { RestrictedProperty, WrappedTokenValue } from './linters';
5
+ const typescriptErrorMessage = 'There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.';
5
6
  const rule = createLintRule({
6
7
  meta: {
7
8
  name: 'use-latest-xcss-syntax-typography',
@@ -14,17 +15,29 @@ const rule = createLintRule({
14
15
  severity: 'warn'
15
16
  },
16
17
  messages: {
17
- noUnsafeTypographyProperties: `Don't set '{{ property }}' on xcss. They are unsafe as they allow invalid combinations of typography tokens. There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.`
18
+ noRestrictedTypographyProperties: `Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ${typescriptErrorMessage}`,
19
+ noRestrictedTypographyPropertiesHeading: `Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ${typescriptErrorMessage}`,
20
+ noWrappedTokenTypographyValues: `Don't wrap typography tokens in xcss. ${typescriptErrorMessage}`
18
21
  }
19
22
  },
20
23
  create(context) {
21
24
  const config = getConfig(context.options[0]);
22
25
  return errorBoundary({
23
- 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': node => BannedProperty.lint(node, {
24
- context
26
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': node => RestrictedProperty.lint(node, {
27
+ context,
28
+ config
25
29
  }),
26
- 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': node => BannedProperty.lint(node, {
27
- context
30
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': node => RestrictedProperty.lint(node, {
31
+ context,
32
+ config
33
+ }),
34
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': node => WrappedTokenValue.lint(node, {
35
+ context,
36
+ config
37
+ }),
38
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(font|fontFamily|fontWeight)/]': node => WrappedTokenValue.lint(node, {
39
+ context,
40
+ config
28
41
  })
29
42
  }, config);
30
43
  }
@@ -1 +1,2 @@
1
- export { BannedProperty } from './banned-property';
1
+ export { RestrictedProperty } from './restricted-property';
2
+ export { WrappedTokenValue } from './wrapped-token-value';
@@ -0,0 +1,61 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ export const RestrictedProperty = {
5
+ lint(node, {
6
+ context,
7
+ config
8
+ }) {
9
+ if (RestrictedProperty._check(node, {
10
+ context,
11
+ config
12
+ })) {
13
+ let property = 'fontSize, lineHeight, fontWeight or letterSpacing';
14
+ if (isNodeOfType(node, 'Identifier')) {
15
+ property = node.name;
16
+ } else if (isNodeOfType(node, 'Literal')) {
17
+ property = String(node.value);
18
+ }
19
+ context.report({
20
+ node,
21
+ messageId: property === 'fontWeight' ? 'noRestrictedTypographyPropertiesHeading' : 'noRestrictedTypographyProperties',
22
+ data: {
23
+ property
24
+ }
25
+ });
26
+ }
27
+ },
28
+ _check(node, {
29
+ config
30
+ }) {
31
+ if (!config.patterns.includes('restricted-property')) {
32
+ return false;
33
+ }
34
+
35
+ // Prevent font weight being used in combination with heading tokens
36
+ if (isNodeOfType(node, 'Identifier') && node.name === 'fontWeight' || isNodeOfType(node, 'Literal') && node.value === 'fontWeight') {
37
+ if (isNodeOfType(node.parent.parent, 'ObjectExpression')) {
38
+ for (const property of node.parent.parent.properties) {
39
+ var _property$value$value;
40
+ // Only looking for heading token on `font` property
41
+ const isFontProperty = isNodeOfType(property, 'Property') && (isNodeOfType(property.key, 'Literal') && property.key.value === 'font' || isNodeOfType(property.key, 'Identifier') && property.key.name === 'font');
42
+ if (!isFontProperty) {
43
+ continue;
44
+ }
45
+
46
+ // Checking for heading token string, for example xcss({ font: 'font.heading.medium' })
47
+ if (isNodeOfType(property.value, 'Literal') && typeof property.value.value === 'string' && (_property$value$value = property.value.value) !== null && _property$value$value !== void 0 && _property$value$value.startsWith('font.heading')) {
48
+ return true;
49
+ }
50
+
51
+ // Checking for wrapped heading token, for example xcss({ font: token('font.heading.medium') })
52
+ if (isNodeOfType(property.value, 'CallExpression') && isNodeOfType(property.value.callee, 'Identifier') && property.value.callee.name === 'token' && isNodeOfType(property.value.arguments[0], 'Literal') && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.startsWith('font.heading')) {
53
+ return true;
54
+ }
55
+ }
56
+ }
57
+ return false;
58
+ }
59
+ return true;
60
+ }
61
+ };
@@ -0,0 +1,44 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ const messageId = 'noWrappedTokenTypographyValues';
5
+ export const WrappedTokenValue = {
6
+ lint(node, {
7
+ context,
8
+ config
9
+ }) {
10
+ if (WrappedTokenValue._check(node, {
11
+ context,
12
+ config
13
+ })) {
14
+ context.report({
15
+ node,
16
+ messageId,
17
+ fix: WrappedTokenValue._fix(node)
18
+ });
19
+ }
20
+ },
21
+ _check(node, {
22
+ config
23
+ }) {
24
+ if (!config.patterns.includes('wrapped-token-value')) {
25
+ return false;
26
+ }
27
+ if (isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'CallExpression') && isNodeOfType(node.parent.value.callee, 'Identifier') && node.parent.value.callee.name === 'token' && node.parent.value.arguments.length >= 1) {
28
+ return true;
29
+ }
30
+ return false;
31
+ },
32
+ _fix(node) {
33
+ return fixer => {
34
+ let wrappedTokenFix;
35
+ if (isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'CallExpression') && node.parent.value.arguments.length >= 1) {
36
+ const firstArg = node.parent.value.arguments[0];
37
+ if (isNodeOfType(firstArg, 'Literal') && typeof firstArg.value === 'string') {
38
+ wrappedTokenFix = fixer.replaceText(node.parent.value, `'${firstArg.value}'`);
39
+ }
40
+ }
41
+ return [wrappedTokenFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
42
+ };
43
+ }
44
+ };
@@ -1,5 +1,6 @@
1
1
  var defaults = {
2
- failSilently: false
2
+ failSilently: false,
3
+ patterns: ['restricted-property', 'wrapped-token-value']
3
4
  };
4
5
  export var getConfig = function getConfig(overrides) {
5
6
  return Object.assign({}, defaults, overrides);
@@ -1,7 +1,8 @@
1
1
  import { createLintRule } from '../utils/create-rule';
2
2
  import { errorBoundary } from '../utils/error-boundary';
3
3
  import { getConfig } from './config';
4
- import { BannedProperty } from './linters';
4
+ import { RestrictedProperty, WrappedTokenValue } from './linters';
5
+ var typescriptErrorMessage = 'There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.';
5
6
  var rule = createLintRule({
6
7
  meta: {
7
8
  name: 'use-latest-xcss-syntax-typography',
@@ -14,20 +15,36 @@ var rule = createLintRule({
14
15
  severity: 'warn'
15
16
  },
16
17
  messages: {
17
- noUnsafeTypographyProperties: "Don't set '{{ property }}' on xcss. They are unsafe as they allow invalid combinations of typography tokens. There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway."
18
+ noRestrictedTypographyProperties: "Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ".concat(typescriptErrorMessage),
19
+ noRestrictedTypographyPropertiesHeading: "Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ".concat(typescriptErrorMessage),
20
+ noWrappedTokenTypographyValues: "Don't wrap typography tokens in xcss. ".concat(typescriptErrorMessage)
18
21
  }
19
22
  },
20
23
  create: function create(context) {
21
24
  var config = getConfig(context.options[0]);
22
25
  return errorBoundary({
23
26
  'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontSizeLineHeightFontWeightLetterSpacing(node) {
24
- return BannedProperty.lint(node, {
25
- context: context
27
+ return RestrictedProperty.lint(node, {
28
+ context: context,
29
+ config: config
26
30
  });
27
31
  },
28
32
  'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(fontSize|lineHeight|fontWeight|letterSpacing)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueFontSizeLineHeightFontWeightLetterSpacing(node) {
29
- return BannedProperty.lint(node, {
30
- context: context
33
+ return RestrictedProperty.lint(node, {
34
+ context: context,
35
+ config: config
36
+ });
37
+ },
38
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontFontFamilyFontWeight(node) {
39
+ return WrappedTokenValue.lint(node, {
40
+ context: context,
41
+ config: config
42
+ });
43
+ },
44
+ 'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueFontFontFamilyFontWeight(node) {
45
+ return WrappedTokenValue.lint(node, {
46
+ context: context,
47
+ config: config
31
48
  });
32
49
  }
33
50
  }, config);
@@ -1 +1,2 @@
1
- export { BannedProperty } from './banned-property';
1
+ export { RestrictedProperty } from './restricted-property';
2
+ export { WrappedTokenValue } from './wrapped-token-value';
@@ -4,10 +4,14 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
4
4
  /* eslint-disable @repo/internal/react/require-jsdoc */
5
5
 
6
6
  import { isNodeOfType } from 'eslint-codemod-utils';
7
- export var BannedProperty = {
7
+ export var RestrictedProperty = {
8
8
  lint: function lint(node, _ref) {
9
- var context = _ref.context;
10
- if (BannedProperty._check(node)) {
9
+ var context = _ref.context,
10
+ config = _ref.config;
11
+ if (RestrictedProperty._check(node, {
12
+ context: context,
13
+ config: config
14
+ })) {
11
15
  var property = 'fontSize, lineHeight, fontWeight or letterSpacing';
12
16
  if (isNodeOfType(node, 'Identifier')) {
13
17
  property = node.name;
@@ -16,14 +20,19 @@ export var BannedProperty = {
16
20
  }
17
21
  context.report({
18
22
  node: node,
19
- messageId: 'noUnsafeTypographyProperties',
23
+ messageId: property === 'fontWeight' ? 'noRestrictedTypographyPropertiesHeading' : 'noRestrictedTypographyProperties',
20
24
  data: {
21
25
  property: property
22
26
  }
23
27
  });
24
28
  }
25
29
  },
26
- _check: function _check(node) {
30
+ _check: function _check(node, _ref2) {
31
+ var config = _ref2.config;
32
+ if (!config.patterns.includes('restricted-property')) {
33
+ return false;
34
+ }
35
+
27
36
  // Prevent font weight being used in combination with heading tokens
28
37
  if (isNodeOfType(node, 'Identifier') && node.name === 'fontWeight' || isNodeOfType(node, 'Literal') && node.value === 'fontWeight') {
29
38
  if (isNodeOfType(node.parent.parent, 'ObjectExpression')) {
@@ -31,11 +40,22 @@ export var BannedProperty = {
31
40
  _step;
32
41
  try {
33
42
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
43
+ var _property$value$value;
34
44
  var property = _step.value;
35
- if (isNodeOfType(property, 'Property') && isNodeOfType(property.value, 'CallExpression') && isNodeOfType(property.value.callee, 'Identifier') && isNodeOfType(property.value.arguments[0], 'Literal')) {
36
- if (property.value.callee.name === 'token' && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.includes('font.heading')) {
37
- return true;
38
- }
45
+ // Only looking for heading token on `font` property
46
+ var isFontProperty = isNodeOfType(property, 'Property') && (isNodeOfType(property.key, 'Literal') && property.key.value === 'font' || isNodeOfType(property.key, 'Identifier') && property.key.name === 'font');
47
+ if (!isFontProperty) {
48
+ continue;
49
+ }
50
+
51
+ // Checking for heading token string, for example xcss({ font: 'font.heading.medium' })
52
+ if (isNodeOfType(property.value, 'Literal') && typeof property.value.value === 'string' && (_property$value$value = property.value.value) !== null && _property$value$value !== void 0 && _property$value$value.startsWith('font.heading')) {
53
+ return true;
54
+ }
55
+
56
+ // Checking for wrapped heading token, for example xcss({ font: token('font.heading.medium') })
57
+ if (isNodeOfType(property.value, 'CallExpression') && isNodeOfType(property.value.callee, 'Identifier') && property.value.callee.name === 'token' && isNodeOfType(property.value.arguments[0], 'Literal') && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.startsWith('font.heading')) {
58
+ return true;
39
59
  }
40
60
  }
41
61
  } catch (err) {
@@ -0,0 +1,44 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ var messageId = 'noWrappedTokenTypographyValues';
5
+ export var WrappedTokenValue = {
6
+ lint: function lint(node, _ref) {
7
+ var context = _ref.context,
8
+ config = _ref.config;
9
+ if (WrappedTokenValue._check(node, {
10
+ context: context,
11
+ config: config
12
+ })) {
13
+ context.report({
14
+ node: node,
15
+ messageId: messageId,
16
+ fix: WrappedTokenValue._fix(node)
17
+ });
18
+ }
19
+ },
20
+ _check: function _check(node, _ref2) {
21
+ var config = _ref2.config;
22
+ if (!config.patterns.includes('wrapped-token-value')) {
23
+ return false;
24
+ }
25
+ if (isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'CallExpression') && isNodeOfType(node.parent.value.callee, 'Identifier') && node.parent.value.callee.name === 'token' && node.parent.value.arguments.length >= 1) {
26
+ return true;
27
+ }
28
+ return false;
29
+ },
30
+ _fix: function _fix(node) {
31
+ return function (fixer) {
32
+ var wrappedTokenFix;
33
+ if (isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'CallExpression') && node.parent.value.arguments.length >= 1) {
34
+ var firstArg = node.parent.value.arguments[0];
35
+ if (isNodeOfType(firstArg, 'Literal') && typeof firstArg.value === 'string') {
36
+ wrappedTokenFix = fixer.replaceText(node.parent.value, "'".concat(firstArg.value, "'"));
37
+ }
38
+ }
39
+ return [wrappedTokenFix].filter(function (fix) {
40
+ return Boolean(fix);
41
+ }); // Some of the transformers can return arrays with undefined, so filter them out
42
+ };
43
+ }
44
+ };
@@ -1,4 +1,7 @@
1
+ type Pattern = 'restricted-property' | 'wrapped-token-value';
1
2
  export interface RuleConfig {
2
3
  failSilently: boolean;
4
+ patterns: Pattern[];
3
5
  }
4
6
  export declare const getConfig: (overrides: Partial<RuleConfig>) => RuleConfig;
7
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type RuleConfig } from '../config';
3
+ export type MetaData = {
4
+ context: Rule.RuleContext;
5
+ config: RuleConfig;
6
+ };
@@ -1 +1,2 @@
1
- export { BannedProperty } from './banned-property';
1
+ export { RestrictedProperty } from './restricted-property';
2
+ export { WrappedTokenValue } from './wrapped-token-value';
@@ -0,0 +1,6 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type MetaData } from './common';
3
+ export declare const RestrictedProperty: {
4
+ lint(node: Rule.Node, { context, config }: MetaData): void;
5
+ _check(node: Rule.Node, { config }: MetaData): boolean;
6
+ };
@@ -0,0 +1,7 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type MetaData } from './common';
3
+ export declare const WrappedTokenValue: {
4
+ lint(node: Rule.Node, { context, config }: MetaData): void;
5
+ _check(node: Rule.Node, { config }: MetaData): boolean;
6
+ _fix(node: Rule.Node): Rule.ReportFixer;
7
+ };
@@ -1,4 +1,7 @@
1
+ type Pattern = 'restricted-property' | 'wrapped-token-value';
1
2
  export interface RuleConfig {
2
3
  failSilently: boolean;
4
+ patterns: Pattern[];
3
5
  }
4
6
  export declare const getConfig: (overrides: Partial<RuleConfig>) => RuleConfig;
7
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type RuleConfig } from '../config';
3
+ export type MetaData = {
4
+ context: Rule.RuleContext;
5
+ config: RuleConfig;
6
+ };
@@ -1 +1,2 @@
1
- export { BannedProperty } from './banned-property';
1
+ export { RestrictedProperty } from './restricted-property';
2
+ export { WrappedTokenValue } from './wrapped-token-value';
@@ -0,0 +1,6 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type MetaData } from './common';
3
+ export declare const RestrictedProperty: {
4
+ lint(node: Rule.Node, { context, config }: MetaData): void;
5
+ _check(node: Rule.Node, { config }: MetaData): boolean;
6
+ };
@@ -0,0 +1,7 @@
1
+ import type { Rule } from 'eslint';
2
+ import { type MetaData } from './common';
3
+ export declare const WrappedTokenValue: {
4
+ lint(node: Rule.Node, { context, config }: MetaData): void;
5
+ _check(node: Rule.Node, { config }: MetaData): boolean;
6
+ _fix(node: Rule.Node): Rule.ReportFixer;
7
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "10.17.3",
4
+ "version": "10.18.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -9,10 +9,6 @@
9
9
  },
10
10
  "atlassian": {
11
11
  "team": "Design System Team",
12
- "releaseModel": "continuous",
13
- "productPushConsumption": [
14
- "jira"
15
- ],
16
12
  "website": {
17
13
  "name": "ESLint plugin",
18
14
  "category": "Tooling"
@@ -1,40 +0,0 @@
1
- /* eslint-disable @repo/internal/react/require-jsdoc */
2
-
3
- import { isNodeOfType } from 'eslint-codemod-utils';
4
- export const BannedProperty = {
5
- lint(node, {
6
- context
7
- }) {
8
- if (BannedProperty._check(node)) {
9
- let property = 'fontSize, lineHeight, fontWeight or letterSpacing';
10
- if (isNodeOfType(node, 'Identifier')) {
11
- property = node.name;
12
- } else if (isNodeOfType(node, 'Literal')) {
13
- property = String(node.value);
14
- }
15
- context.report({
16
- node,
17
- messageId: 'noUnsafeTypographyProperties',
18
- data: {
19
- property
20
- }
21
- });
22
- }
23
- },
24
- _check(node) {
25
- // Prevent font weight being used in combination with heading tokens
26
- if (isNodeOfType(node, 'Identifier') && node.name === 'fontWeight' || isNodeOfType(node, 'Literal') && node.value === 'fontWeight') {
27
- if (isNodeOfType(node.parent.parent, 'ObjectExpression')) {
28
- for (const property of node.parent.parent.properties) {
29
- if (isNodeOfType(property, 'Property') && isNodeOfType(property.value, 'CallExpression') && isNodeOfType(property.value.callee, 'Identifier') && isNodeOfType(property.value.arguments[0], 'Literal')) {
30
- if (property.value.callee.name === 'token' && typeof property.value.arguments[0].value === 'string' && property.value.arguments[0].value.includes('font.heading')) {
31
- return true;
32
- }
33
- }
34
- }
35
- }
36
- return false;
37
- }
38
- return true;
39
- }
40
- };
@@ -1,7 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- export declare const BannedProperty: {
3
- lint(node: Rule.Node, { context }: {
4
- context: Rule.RuleContext;
5
- }): void;
6
- _check(node: Rule.Node): boolean;
7
- };
@@ -1,7 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- export declare const BannedProperty: {
3
- lint(node: Rule.Node, { context }: {
4
- context: Rule.RuleContext;
5
- }): void;
6
- _check(node: Rule.Node): boolean;
7
- };