@atlaskit/eslint-plugin-design-system 13.26.0 → 13.27.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 (28) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/presets/all-flat.codegen.js +3 -1
  4. package/dist/cjs/presets/all.codegen.js +3 -1
  5. package/dist/cjs/rules/index.codegen.js +5 -1
  6. package/dist/cjs/rules/no-to-match-snapshot/index.js +49 -0
  7. package/dist/cjs/rules/no-unsafe-inline-snapshot/index.js +139 -0
  8. package/dist/es2019/presets/all-flat.codegen.js +3 -1
  9. package/dist/es2019/presets/all.codegen.js +3 -1
  10. package/dist/es2019/rules/index.codegen.js +5 -1
  11. package/dist/es2019/rules/no-to-match-snapshot/index.js +43 -0
  12. package/dist/es2019/rules/no-unsafe-inline-snapshot/index.js +134 -0
  13. package/dist/esm/presets/all-flat.codegen.js +3 -1
  14. package/dist/esm/presets/all.codegen.js +3 -1
  15. package/dist/esm/rules/index.codegen.js +5 -1
  16. package/dist/esm/rules/no-to-match-snapshot/index.js +43 -0
  17. package/dist/esm/rules/no-unsafe-inline-snapshot/index.js +133 -0
  18. package/dist/types/presets/all-flat.codegen.d.ts +1 -1
  19. package/dist/types/presets/all.codegen.d.ts +1 -1
  20. package/dist/types/rules/index.codegen.d.ts +1 -1
  21. package/dist/types/rules/no-to-match-snapshot/index.d.ts +4 -0
  22. package/dist/types/rules/no-unsafe-inline-snapshot/index.d.ts +4 -0
  23. package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -1
  24. package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -1
  25. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
  26. package/dist/types-ts4.5/rules/no-to-match-snapshot/index.d.ts +4 -0
  27. package/dist/types-ts4.5/rules/no-unsafe-inline-snapshot/index.d.ts +4 -0
  28. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 13.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`32751f9d05b33`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/32751f9d05b33) -
8
+ Introduces two new rules which guard against improper usage of `.toMatchSnapshot` and
9
+ `.toMatchInlineSnapshot`
10
+
3
11
  ## 13.26.0
4
12
 
5
13
  ### Minor Changes
package/README.md CHANGED
@@ -89,7 +89,9 @@ module.exports = {
89
89
  | <a href="./src/rules/no-physical-properties/README.md">no-physical-properties</a> | Disallow physical properties and values in `css` and `cssMap` function calls. | | Yes | |
90
90
  | <a href="./src/rules/no-separator-with-list-elements/README.md">no-separator-with-list-elements</a> | Warn when the `separator` prop is used with `as="li"`, `as="ol"`, or `as="dl"` in the Inline component. | Yes | | |
91
91
  | <a href="./src/rules/no-styled-tagged-template-expression/README.md">no-styled-tagged-template-expression</a> | Disallows any `styled` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
92
+ | <a href="./src/rules/no-to-match-snapshot/README.md">no-to-match-snapshot</a> | Disallow using toMatchSnapshot() in favor of toMatchInlineSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale. | | | |
92
93
  | <a href="./src/rules/no-unsafe-design-token-usage/README.md">no-unsafe-design-token-usage</a> | Enforces design token usage is statically and locally analyzable. | Yes | Yes | |
94
+ | <a href="./src/rules/no-unsafe-inline-snapshot/README.md">no-unsafe-inline-snapshot</a> | Enforce guardrails on toMatchInlineSnapshot usage: snapshots must not exceed 100 lines and must not contain internal implementation details like className or style attributes. | | | |
93
95
  | <a href="./src/rules/no-unsafe-style-overrides/README.md">no-unsafe-style-overrides</a> | Discourage usage of unsafe style overrides used against the Atlassian Design System. | Yes | | |
94
96
  | <a href="./src/rules/no-unsupported-drag-and-drop-libraries/README.md">no-unsupported-drag-and-drop-libraries</a> | Disallow importing unsupported drag and drop modules. | Yes | | |
95
97
  | <a href="./src/rules/no-unused-css-map/README.md">no-unused-css-map</a> | Detects unused styles in cssMap objects to help keep code clean. | Yes | | |
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  /**
8
8
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
9
- * @codegen <<SignedSource::df988d512376c1317e5f7a350ad19fb9>>
9
+ * @codegen <<SignedSource::d7a0407a6c6b10bfbba790523d06f97d>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
 
@@ -53,7 +53,9 @@ var rules = {
53
53
  '@atlaskit/design-system/no-physical-properties': 'error',
54
54
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
55
55
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
56
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
56
57
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
58
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
57
59
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
58
60
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
59
61
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  /**
8
8
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
9
- * @codegen <<SignedSource::2dee8518d733b0b364809d0f5641afda>>
9
+ * @codegen <<SignedSource::e632a96e9bae920c23e25114f40e3c0d>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
 
@@ -52,7 +52,9 @@ var rules = {
52
52
  '@atlaskit/design-system/no-physical-properties': 'error',
53
53
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
54
54
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
55
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
55
56
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
57
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
56
58
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
57
59
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
58
60
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -45,7 +45,9 @@ var _noNestedStyles = _interopRequireDefault(require("./no-nested-styles"));
45
45
  var _noPhysicalProperties = _interopRequireDefault(require("./no-physical-properties"));
46
46
  var _noSeparatorWithListElements = _interopRequireDefault(require("./no-separator-with-list-elements"));
47
47
  var _noStyledTaggedTemplateExpression = _interopRequireDefault(require("./no-styled-tagged-template-expression"));
48
+ var _noToMatchSnapshot = _interopRequireDefault(require("./no-to-match-snapshot"));
48
49
  var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./no-unsafe-design-token-usage"));
50
+ var _noUnsafeInlineSnapshot = _interopRequireDefault(require("./no-unsafe-inline-snapshot"));
49
51
  var _noUnsafeStyleOverrides = _interopRequireDefault(require("./no-unsafe-style-overrides"));
50
52
  var _noUnsupportedDragAndDropLibraries = _interopRequireDefault(require("./no-unsupported-drag-and-drop-libraries"));
51
53
  var _noUnusedCssMap = _interopRequireDefault(require("./no-unused-css-map"));
@@ -76,7 +78,7 @@ var _useTokensTypography = _interopRequireDefault(require("./use-tokens-typograp
76
78
  var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
77
79
  /**
78
80
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
79
- * @codegen <<SignedSource::c74b833590d4eb693616a3fcbf2e335a>>
81
+ * @codegen <<SignedSource::62347cf64e4ac99b927fce9d1a2bd894>>
80
82
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
81
83
  */
82
84
 
@@ -121,7 +123,9 @@ var rules = exports.rules = {
121
123
  'no-physical-properties': _noPhysicalProperties.default,
122
124
  'no-separator-with-list-elements': _noSeparatorWithListElements.default,
123
125
  'no-styled-tagged-template-expression': _noStyledTaggedTemplateExpression.default,
126
+ 'no-to-match-snapshot': _noToMatchSnapshot.default,
124
127
  'no-unsafe-design-token-usage': _noUnsafeDesignTokenUsage.default,
128
+ 'no-unsafe-inline-snapshot': _noUnsafeInlineSnapshot.default,
125
129
  'no-unsafe-style-overrides': _noUnsafeStyleOverrides.default,
126
130
  'no-unsupported-drag-and-drop-libraries': _noUnsupportedDragAndDropLibraries.default,
127
131
  'no-unused-css-map': _noUnusedCssMap.default,
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.name = exports.default = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ var _createRule = require("../utils/create-rule");
9
+ var name = exports.name = 'no-to-match-snapshot';
10
+ var rule = (0, _createRule.createLintRule)({
11
+ meta: {
12
+ name: name,
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Disallow using toMatchSnapshot() in favor of toMatchInlineSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.',
16
+ recommended: false,
17
+ severity: 'error'
18
+ },
19
+ messages: {
20
+ useInlineSnapshot: 'Use toMatchInlineSnapshot() instead of toMatchSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.'
21
+ }
22
+ },
23
+ create: function create(context) {
24
+ return {
25
+ MemberExpression: function MemberExpression(node) {
26
+ // Check if this is a call to toMatchSnapshot
27
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.property, 'Identifier') || node.property.name !== 'toMatchSnapshot') {
28
+ return;
29
+ }
30
+
31
+ // Check if the object is an expect() call
32
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.object, 'CallExpression') || !(0, _eslintCodemodUtils.isNodeOfType)(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
33
+ return;
34
+ }
35
+
36
+ // Only report if this is being called (i.e., it's part of a CallExpression)
37
+ // We want to catch expect(...).toMatchSnapshot() but not just the property access
38
+ if (!node.parent || !(0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'CallExpression')) {
39
+ return;
40
+ }
41
+ context.report({
42
+ node: node.property,
43
+ messageId: 'useInlineSnapshot'
44
+ });
45
+ }
46
+ };
47
+ }
48
+ });
49
+ var _default = exports.default = rule;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.name = exports.default = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ var _contextCompat = require("@atlaskit/eslint-utils/context-compat");
9
+ var _createRule = require("../utils/create-rule");
10
+ var name = exports.name = 'no-unsafe-inline-snapshot';
11
+ var MAX_LINES = 100;
12
+
13
+ /**
14
+ * Checks if a snapshot contains internal implementation details
15
+ */
16
+ function containsInternalDetails(snapshotContent) {
17
+ var issues = [];
18
+
19
+ // Check for className attributes (unless they equal "REDACTED")
20
+ // Handles: className="value", className='value', and whitespace variations
21
+ var classNameRegex = /className\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
22
+ var match;
23
+ while ((match = classNameRegex.exec(snapshotContent)) !== null) {
24
+ var classNameValue = match[2];
25
+ if (classNameValue && classNameValue !== 'REDACTED') {
26
+ issues.push("className=\"".concat(classNameValue, "\""));
27
+ }
28
+ }
29
+
30
+ // Check for style attributes (unless they equal "REDACTED")
31
+ // Handles: style="value", style='value', and whitespace variations
32
+ // Style values can contain colons, semicolons, etc., so we need to capture the full quoted value
33
+ var styleRegex = /style\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
34
+ while ((match = styleRegex.exec(snapshotContent)) !== null) {
35
+ var styleValue = match[2];
36
+ if (styleValue && styleValue !== 'REDACTED') {
37
+ issues.push("style=\"".concat(styleValue, "\""));
38
+ }
39
+ }
40
+
41
+ // Check for style blocks (unless they contain "REDACTED")
42
+ var styleBlockRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
43
+ while ((match = styleBlockRegex.exec(snapshotContent)) !== null) {
44
+ var styleContent = match[1];
45
+ if (styleContent && !styleContent.trim().includes('REDACTED')) {
46
+ issues.push('style block');
47
+ }
48
+ }
49
+ return {
50
+ hasIssues: issues.length > 0,
51
+ issues: issues
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Extracts the snapshot content from a template literal or string literal
57
+ */
58
+ function extractSnapshotContent(node, sourceCode) {
59
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'TemplateLiteral')) {
60
+ // For template literals, get the raw text including the template parts
61
+ return sourceCode.getText(node);
62
+ }
63
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') && typeof node.value === 'string') {
64
+ return node.value;
65
+ }
66
+ return null;
67
+ }
68
+ var rule = (0, _createRule.createLintRule)({
69
+ meta: {
70
+ name: name,
71
+ type: 'problem',
72
+ docs: {
73
+ description: 'Enforce guardrails on toMatchInlineSnapshot usage: snapshots must not exceed 100 lines and must not contain internal implementation details like className or style attributes.',
74
+ recommended: false,
75
+ severity: 'error'
76
+ },
77
+ messages: {
78
+ exceedsMaxLines: "Inline snapshot exceeds ".concat(MAX_LINES, " lines. Consider breaking it into smaller snapshots or using a different testing approach."),
79
+ containsInternalDetails: 'Inline snapshot contains internal implementation details: {{details}}. Use "REDACTED" for className and style values, or remove these details from the snapshot.'
80
+ }
81
+ },
82
+ create: function create(context) {
83
+ var sourceCode = (0, _contextCompat.getSourceCode)(context);
84
+ return {
85
+ MemberExpression: function MemberExpression(node) {
86
+ // Check if this is a call to toMatchInlineSnapshot
87
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.property, 'Identifier') || node.property.name !== 'toMatchInlineSnapshot') {
88
+ return;
89
+ }
90
+
91
+ // Check if the object is an expect() call
92
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.object, 'CallExpression') || !(0, _eslintCodemodUtils.isNodeOfType)(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
93
+ return;
94
+ }
95
+
96
+ // Only report if this is being called (i.e., it's part of a CallExpression)
97
+ if (!node.parent || !(0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'CallExpression')) {
98
+ return;
99
+ }
100
+
101
+ // Get the snapshot content from the first argument
102
+ var callExpression = node.parent;
103
+ if (callExpression.arguments.length === 0) {
104
+ return;
105
+ }
106
+ var snapshotArg = callExpression.arguments[0];
107
+ var snapshotContent = extractSnapshotContent(snapshotArg, sourceCode);
108
+ if (!snapshotContent) {
109
+ return;
110
+ }
111
+
112
+ // Check line count
113
+ var lines = snapshotContent.split('\n');
114
+ if (lines.length > MAX_LINES) {
115
+ context.report({
116
+ node: snapshotArg,
117
+ messageId: 'exceedsMaxLines'
118
+ });
119
+ return;
120
+ }
121
+
122
+ // Check for internal implementation details
123
+ var _containsInternalDeta = containsInternalDetails(snapshotContent),
124
+ hasIssues = _containsInternalDeta.hasIssues,
125
+ issues = _containsInternalDeta.issues;
126
+ if (hasIssues) {
127
+ context.report({
128
+ node: snapshotArg,
129
+ messageId: 'containsInternalDetails',
130
+ data: {
131
+ details: issues.slice(0, 3).join(', ') // Show first 3 issues
132
+ }
133
+ });
134
+ }
135
+ }
136
+ };
137
+ }
138
+ });
139
+ var _default = exports.default = rule;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::df988d512376c1317e5f7a350ad19fb9>>
3
+ * @codegen <<SignedSource::d7a0407a6c6b10bfbba790523d06f97d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -47,7 +47,9 @@ const rules = {
47
47
  '@atlaskit/design-system/no-physical-properties': 'error',
48
48
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
49
49
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
50
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
50
51
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
52
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
51
53
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
52
54
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
53
55
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::2dee8518d733b0b364809d0f5641afda>>
3
+ * @codegen <<SignedSource::e632a96e9bae920c23e25114f40e3c0d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -46,7 +46,9 @@ const rules = {
46
46
  '@atlaskit/design-system/no-physical-properties': 'error',
47
47
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
48
48
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
49
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
49
50
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
51
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
50
52
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
51
53
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
52
54
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::c74b833590d4eb693616a3fcbf2e335a>>
3
+ * @codegen <<SignedSource::62347cf64e4ac99b927fce9d1a2bd894>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -44,7 +44,9 @@ import noNestedStyles from './no-nested-styles';
44
44
  import noPhysicalProperties from './no-physical-properties';
45
45
  import noSeparatorWithListElements from './no-separator-with-list-elements';
46
46
  import noStyledTaggedTemplateExpression from './no-styled-tagged-template-expression';
47
+ import noToMatchSnapshot from './no-to-match-snapshot';
47
48
  import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
49
+ import noUnsafeInlineSnapshot from './no-unsafe-inline-snapshot';
48
50
  import noUnsafeStyleOverrides from './no-unsafe-style-overrides';
49
51
  import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-libraries';
50
52
  import noUnusedCssMap from './no-unused-css-map';
@@ -114,7 +116,9 @@ export const rules = {
114
116
  'no-physical-properties': noPhysicalProperties,
115
117
  'no-separator-with-list-elements': noSeparatorWithListElements,
116
118
  'no-styled-tagged-template-expression': noStyledTaggedTemplateExpression,
119
+ 'no-to-match-snapshot': noToMatchSnapshot,
117
120
  'no-unsafe-design-token-usage': noUnsafeDesignTokenUsage,
121
+ 'no-unsafe-inline-snapshot': noUnsafeInlineSnapshot,
118
122
  'no-unsafe-style-overrides': noUnsafeStyleOverrides,
119
123
  'no-unsupported-drag-and-drop-libraries': noUnsupportedDragAndDropLibraries,
120
124
  'no-unused-css-map': noUnusedCssMap,
@@ -0,0 +1,43 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ export const name = 'no-to-match-snapshot';
4
+ const rule = createLintRule({
5
+ meta: {
6
+ name,
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Disallow using toMatchSnapshot() in favor of toMatchInlineSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.',
10
+ recommended: false,
11
+ severity: 'error'
12
+ },
13
+ messages: {
14
+ useInlineSnapshot: 'Use toMatchInlineSnapshot() instead of toMatchSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.'
15
+ }
16
+ },
17
+ create(context) {
18
+ return {
19
+ MemberExpression(node) {
20
+ // Check if this is a call to toMatchSnapshot
21
+ if (!isNodeOfType(node.property, 'Identifier') || node.property.name !== 'toMatchSnapshot') {
22
+ return;
23
+ }
24
+
25
+ // Check if the object is an expect() call
26
+ if (!isNodeOfType(node.object, 'CallExpression') || !isNodeOfType(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
27
+ return;
28
+ }
29
+
30
+ // Only report if this is being called (i.e., it's part of a CallExpression)
31
+ // We want to catch expect(...).toMatchSnapshot() but not just the property access
32
+ if (!node.parent || !isNodeOfType(node.parent, 'CallExpression')) {
33
+ return;
34
+ }
35
+ context.report({
36
+ node: node.property,
37
+ messageId: 'useInlineSnapshot'
38
+ });
39
+ }
40
+ };
41
+ }
42
+ });
43
+ export default rule;
@@ -0,0 +1,134 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { getSourceCode } from '@atlaskit/eslint-utils/context-compat';
3
+ import { createLintRule } from '../utils/create-rule';
4
+ export const name = 'no-unsafe-inline-snapshot';
5
+ const MAX_LINES = 100;
6
+
7
+ /**
8
+ * Checks if a snapshot contains internal implementation details
9
+ */
10
+ function containsInternalDetails(snapshotContent) {
11
+ const issues = [];
12
+
13
+ // Check for className attributes (unless they equal "REDACTED")
14
+ // Handles: className="value", className='value', and whitespace variations
15
+ const classNameRegex = /className\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
16
+ let match;
17
+ while ((match = classNameRegex.exec(snapshotContent)) !== null) {
18
+ const classNameValue = match[2];
19
+ if (classNameValue && classNameValue !== 'REDACTED') {
20
+ issues.push(`className="${classNameValue}"`);
21
+ }
22
+ }
23
+
24
+ // Check for style attributes (unless they equal "REDACTED")
25
+ // Handles: style="value", style='value', and whitespace variations
26
+ // Style values can contain colons, semicolons, etc., so we need to capture the full quoted value
27
+ const styleRegex = /style\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
28
+ while ((match = styleRegex.exec(snapshotContent)) !== null) {
29
+ const styleValue = match[2];
30
+ if (styleValue && styleValue !== 'REDACTED') {
31
+ issues.push(`style="${styleValue}"`);
32
+ }
33
+ }
34
+
35
+ // Check for style blocks (unless they contain "REDACTED")
36
+ const styleBlockRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
37
+ while ((match = styleBlockRegex.exec(snapshotContent)) !== null) {
38
+ const styleContent = match[1];
39
+ if (styleContent && !styleContent.trim().includes('REDACTED')) {
40
+ issues.push('style block');
41
+ }
42
+ }
43
+ return {
44
+ hasIssues: issues.length > 0,
45
+ issues
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Extracts the snapshot content from a template literal or string literal
51
+ */
52
+ function extractSnapshotContent(node, sourceCode) {
53
+ if (isNodeOfType(node, 'TemplateLiteral')) {
54
+ // For template literals, get the raw text including the template parts
55
+ return sourceCode.getText(node);
56
+ }
57
+ if (isNodeOfType(node, 'Literal') && typeof node.value === 'string') {
58
+ return node.value;
59
+ }
60
+ return null;
61
+ }
62
+ const rule = createLintRule({
63
+ meta: {
64
+ name,
65
+ type: 'problem',
66
+ docs: {
67
+ description: 'Enforce guardrails on toMatchInlineSnapshot usage: snapshots must not exceed 100 lines and must not contain internal implementation details like className or style attributes.',
68
+ recommended: false,
69
+ severity: 'error'
70
+ },
71
+ messages: {
72
+ exceedsMaxLines: `Inline snapshot exceeds ${MAX_LINES} lines. Consider breaking it into smaller snapshots or using a different testing approach.`,
73
+ containsInternalDetails: 'Inline snapshot contains internal implementation details: {{details}}. Use "REDACTED" for className and style values, or remove these details from the snapshot.'
74
+ }
75
+ },
76
+ create(context) {
77
+ const sourceCode = getSourceCode(context);
78
+ return {
79
+ MemberExpression(node) {
80
+ // Check if this is a call to toMatchInlineSnapshot
81
+ if (!isNodeOfType(node.property, 'Identifier') || node.property.name !== 'toMatchInlineSnapshot') {
82
+ return;
83
+ }
84
+
85
+ // Check if the object is an expect() call
86
+ if (!isNodeOfType(node.object, 'CallExpression') || !isNodeOfType(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
87
+ return;
88
+ }
89
+
90
+ // Only report if this is being called (i.e., it's part of a CallExpression)
91
+ if (!node.parent || !isNodeOfType(node.parent, 'CallExpression')) {
92
+ return;
93
+ }
94
+
95
+ // Get the snapshot content from the first argument
96
+ const callExpression = node.parent;
97
+ if (callExpression.arguments.length === 0) {
98
+ return;
99
+ }
100
+ const snapshotArg = callExpression.arguments[0];
101
+ const snapshotContent = extractSnapshotContent(snapshotArg, sourceCode);
102
+ if (!snapshotContent) {
103
+ return;
104
+ }
105
+
106
+ // Check line count
107
+ const lines = snapshotContent.split('\n');
108
+ if (lines.length > MAX_LINES) {
109
+ context.report({
110
+ node: snapshotArg,
111
+ messageId: 'exceedsMaxLines'
112
+ });
113
+ return;
114
+ }
115
+
116
+ // Check for internal implementation details
117
+ const {
118
+ hasIssues,
119
+ issues
120
+ } = containsInternalDetails(snapshotContent);
121
+ if (hasIssues) {
122
+ context.report({
123
+ node: snapshotArg,
124
+ messageId: 'containsInternalDetails',
125
+ data: {
126
+ details: issues.slice(0, 3).join(', ') // Show first 3 issues
127
+ }
128
+ });
129
+ }
130
+ }
131
+ };
132
+ }
133
+ });
134
+ export default rule;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::df988d512376c1317e5f7a350ad19fb9>>
3
+ * @codegen <<SignedSource::d7a0407a6c6b10bfbba790523d06f97d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -47,7 +47,9 @@ var rules = {
47
47
  '@atlaskit/design-system/no-physical-properties': 'error',
48
48
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
49
49
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
50
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
50
51
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
52
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
51
53
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
52
54
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
53
55
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::2dee8518d733b0b364809d0f5641afda>>
3
+ * @codegen <<SignedSource::e632a96e9bae920c23e25114f40e3c0d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -46,7 +46,9 @@ var rules = {
46
46
  '@atlaskit/design-system/no-physical-properties': 'error',
47
47
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
48
48
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
49
+ '@atlaskit/design-system/no-to-match-snapshot': 'error',
49
50
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
51
+ '@atlaskit/design-system/no-unsafe-inline-snapshot': 'error',
50
52
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
51
53
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
52
54
  '@atlaskit/design-system/no-unused-css-map': 'warn',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::c74b833590d4eb693616a3fcbf2e335a>>
3
+ * @codegen <<SignedSource::62347cf64e4ac99b927fce9d1a2bd894>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -44,7 +44,9 @@ import noNestedStyles from './no-nested-styles';
44
44
  import noPhysicalProperties from './no-physical-properties';
45
45
  import noSeparatorWithListElements from './no-separator-with-list-elements';
46
46
  import noStyledTaggedTemplateExpression from './no-styled-tagged-template-expression';
47
+ import noToMatchSnapshot from './no-to-match-snapshot';
47
48
  import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
49
+ import noUnsafeInlineSnapshot from './no-unsafe-inline-snapshot';
48
50
  import noUnsafeStyleOverrides from './no-unsafe-style-overrides';
49
51
  import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-libraries';
50
52
  import noUnusedCssMap from './no-unused-css-map';
@@ -114,7 +116,9 @@ export var rules = {
114
116
  'no-physical-properties': noPhysicalProperties,
115
117
  'no-separator-with-list-elements': noSeparatorWithListElements,
116
118
  'no-styled-tagged-template-expression': noStyledTaggedTemplateExpression,
119
+ 'no-to-match-snapshot': noToMatchSnapshot,
117
120
  'no-unsafe-design-token-usage': noUnsafeDesignTokenUsage,
121
+ 'no-unsafe-inline-snapshot': noUnsafeInlineSnapshot,
118
122
  'no-unsafe-style-overrides': noUnsafeStyleOverrides,
119
123
  'no-unsupported-drag-and-drop-libraries': noUnsupportedDragAndDropLibraries,
120
124
  'no-unused-css-map': noUnusedCssMap,
@@ -0,0 +1,43 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ export var name = 'no-to-match-snapshot';
4
+ var rule = createLintRule({
5
+ meta: {
6
+ name: name,
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Disallow using toMatchSnapshot() in favor of toMatchInlineSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.',
10
+ recommended: false,
11
+ severity: 'error'
12
+ },
13
+ messages: {
14
+ useInlineSnapshot: 'Use toMatchInlineSnapshot() instead of toMatchSnapshot(). See https://hello.atlassian.net/wiki/spaces/DST/pages/6105892000/DSTRFC-038+-+Removal+of+.toMatchSnapshot for rationale.'
15
+ }
16
+ },
17
+ create: function create(context) {
18
+ return {
19
+ MemberExpression: function MemberExpression(node) {
20
+ // Check if this is a call to toMatchSnapshot
21
+ if (!isNodeOfType(node.property, 'Identifier') || node.property.name !== 'toMatchSnapshot') {
22
+ return;
23
+ }
24
+
25
+ // Check if the object is an expect() call
26
+ if (!isNodeOfType(node.object, 'CallExpression') || !isNodeOfType(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
27
+ return;
28
+ }
29
+
30
+ // Only report if this is being called (i.e., it's part of a CallExpression)
31
+ // We want to catch expect(...).toMatchSnapshot() but not just the property access
32
+ if (!node.parent || !isNodeOfType(node.parent, 'CallExpression')) {
33
+ return;
34
+ }
35
+ context.report({
36
+ node: node.property,
37
+ messageId: 'useInlineSnapshot'
38
+ });
39
+ }
40
+ };
41
+ }
42
+ });
43
+ export default rule;
@@ -0,0 +1,133 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { getSourceCode } from '@atlaskit/eslint-utils/context-compat';
3
+ import { createLintRule } from '../utils/create-rule';
4
+ export var name = 'no-unsafe-inline-snapshot';
5
+ var MAX_LINES = 100;
6
+
7
+ /**
8
+ * Checks if a snapshot contains internal implementation details
9
+ */
10
+ function containsInternalDetails(snapshotContent) {
11
+ var issues = [];
12
+
13
+ // Check for className attributes (unless they equal "REDACTED")
14
+ // Handles: className="value", className='value', and whitespace variations
15
+ var classNameRegex = /className\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
16
+ var match;
17
+ while ((match = classNameRegex.exec(snapshotContent)) !== null) {
18
+ var classNameValue = match[2];
19
+ if (classNameValue && classNameValue !== 'REDACTED') {
20
+ issues.push("className=\"".concat(classNameValue, "\""));
21
+ }
22
+ }
23
+
24
+ // Check for style attributes (unless they equal "REDACTED")
25
+ // Handles: style="value", style='value', and whitespace variations
26
+ // Style values can contain colons, semicolons, etc., so we need to capture the full quoted value
27
+ var styleRegex = /style\s*=\s*(["'])((?:(?!\1)[^\\]|\\.)*)\1/gi;
28
+ while ((match = styleRegex.exec(snapshotContent)) !== null) {
29
+ var styleValue = match[2];
30
+ if (styleValue && styleValue !== 'REDACTED') {
31
+ issues.push("style=\"".concat(styleValue, "\""));
32
+ }
33
+ }
34
+
35
+ // Check for style blocks (unless they contain "REDACTED")
36
+ var styleBlockRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
37
+ while ((match = styleBlockRegex.exec(snapshotContent)) !== null) {
38
+ var styleContent = match[1];
39
+ if (styleContent && !styleContent.trim().includes('REDACTED')) {
40
+ issues.push('style block');
41
+ }
42
+ }
43
+ return {
44
+ hasIssues: issues.length > 0,
45
+ issues: issues
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Extracts the snapshot content from a template literal or string literal
51
+ */
52
+ function extractSnapshotContent(node, sourceCode) {
53
+ if (isNodeOfType(node, 'TemplateLiteral')) {
54
+ // For template literals, get the raw text including the template parts
55
+ return sourceCode.getText(node);
56
+ }
57
+ if (isNodeOfType(node, 'Literal') && typeof node.value === 'string') {
58
+ return node.value;
59
+ }
60
+ return null;
61
+ }
62
+ var rule = createLintRule({
63
+ meta: {
64
+ name: name,
65
+ type: 'problem',
66
+ docs: {
67
+ description: 'Enforce guardrails on toMatchInlineSnapshot usage: snapshots must not exceed 100 lines and must not contain internal implementation details like className or style attributes.',
68
+ recommended: false,
69
+ severity: 'error'
70
+ },
71
+ messages: {
72
+ exceedsMaxLines: "Inline snapshot exceeds ".concat(MAX_LINES, " lines. Consider breaking it into smaller snapshots or using a different testing approach."),
73
+ containsInternalDetails: 'Inline snapshot contains internal implementation details: {{details}}. Use "REDACTED" for className and style values, or remove these details from the snapshot.'
74
+ }
75
+ },
76
+ create: function create(context) {
77
+ var sourceCode = getSourceCode(context);
78
+ return {
79
+ MemberExpression: function MemberExpression(node) {
80
+ // Check if this is a call to toMatchInlineSnapshot
81
+ if (!isNodeOfType(node.property, 'Identifier') || node.property.name !== 'toMatchInlineSnapshot') {
82
+ return;
83
+ }
84
+
85
+ // Check if the object is an expect() call
86
+ if (!isNodeOfType(node.object, 'CallExpression') || !isNodeOfType(node.object.callee, 'Identifier') || node.object.callee.name !== 'expect') {
87
+ return;
88
+ }
89
+
90
+ // Only report if this is being called (i.e., it's part of a CallExpression)
91
+ if (!node.parent || !isNodeOfType(node.parent, 'CallExpression')) {
92
+ return;
93
+ }
94
+
95
+ // Get the snapshot content from the first argument
96
+ var callExpression = node.parent;
97
+ if (callExpression.arguments.length === 0) {
98
+ return;
99
+ }
100
+ var snapshotArg = callExpression.arguments[0];
101
+ var snapshotContent = extractSnapshotContent(snapshotArg, sourceCode);
102
+ if (!snapshotContent) {
103
+ return;
104
+ }
105
+
106
+ // Check line count
107
+ var lines = snapshotContent.split('\n');
108
+ if (lines.length > MAX_LINES) {
109
+ context.report({
110
+ node: snapshotArg,
111
+ messageId: 'exceedsMaxLines'
112
+ });
113
+ return;
114
+ }
115
+
116
+ // Check for internal implementation details
117
+ var _containsInternalDeta = containsInternalDetails(snapshotContent),
118
+ hasIssues = _containsInternalDeta.hasIssues,
119
+ issues = _containsInternalDeta.issues;
120
+ if (hasIssues) {
121
+ context.report({
122
+ node: snapshotArg,
123
+ messageId: 'containsInternalDetails',
124
+ data: {
125
+ details: issues.slice(0, 3).join(', ') // Show first 3 issues
126
+ }
127
+ });
128
+ }
129
+ }
130
+ };
131
+ }
132
+ });
133
+ export default rule;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::df988d512376c1317e5f7a350ad19fb9>>
3
+ * @codegen <<SignedSource::d7a0407a6c6b10bfbba790523d06f97d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Linter } from 'eslint';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::2dee8518d733b0b364809d0f5641afda>>
3
+ * @codegen <<SignedSource::e632a96e9bae920c23e25114f40e3c0d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { ESLint } from 'eslint';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::c74b833590d4eb693616a3fcbf2e335a>>
3
+ * @codegen <<SignedSource::62347cf64e4ac99b927fce9d1a2bd894>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Rule } from 'eslint';
@@ -0,0 +1,4 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const name = "no-to-match-snapshot";
3
+ declare const rule: Rule.RuleModule;
4
+ export default rule;
@@ -0,0 +1,4 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const name = "no-unsafe-inline-snapshot";
3
+ declare const rule: Rule.RuleModule;
4
+ export default rule;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::df988d512376c1317e5f7a350ad19fb9>>
3
+ * @codegen <<SignedSource::d7a0407a6c6b10bfbba790523d06f97d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Linter } from 'eslint';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::2dee8518d733b0b364809d0f5641afda>>
3
+ * @codegen <<SignedSource::e632a96e9bae920c23e25114f40e3c0d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { ESLint } from 'eslint';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::c74b833590d4eb693616a3fcbf2e335a>>
3
+ * @codegen <<SignedSource::62347cf64e4ac99b927fce9d1a2bd894>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Rule } from 'eslint';
@@ -0,0 +1,4 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const name = "no-to-match-snapshot";
3
+ declare const rule: Rule.RuleModule;
4
+ export default rule;
@@ -0,0 +1,4 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const name = "no-unsafe-inline-snapshot";
3
+ declare const rule: Rule.RuleModule;
4
+ export default rule;
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": "13.26.0",
4
+ "version": "13.27.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -41,7 +41,7 @@
41
41
  "@atlaskit/eslint-utils": "^2.0.0",
42
42
  "@atlaskit/icon": "^29.0.0",
43
43
  "@atlaskit/icon-lab": "^5.12.0",
44
- "@atlaskit/tokens": "^8.2.0",
44
+ "@atlaskit/tokens": "^8.4.0",
45
45
  "@babel/runtime": "^7.0.0",
46
46
  "@typescript-eslint/utils": "^7.1.0",
47
47
  "ajv": "^6.12.6",