@atlaskit/eslint-plugin-platform 0.0.5 → 0.0.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/eslint-plugin-platform
2
2
 
3
+ ## 0.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [`0cab60b90c3`](https://bitbucket.org/atlassian/atlassian-frontend/commits/0cab60b90c3) - Add fix to eslint rule on the arguments of nested test runner
8
+
9
+ ## 0.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - [`99449cce7f5`](https://bitbucket.org/atlassian/atlassian-frontend/commits/99449cce7f5) - Eslint rules around test runner arguments and limit on nested test runners
14
+
3
15
  ## 0.0.5
4
16
 
5
17
  ### Patch Changes
package/dist/cjs/index.js CHANGED
@@ -6,9 +6,13 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.rules = exports.configs = void 0;
8
8
  var _ensureFeatureFlagRegistration = _interopRequireDefault(require("./rules/ensure-feature-flag-registration"));
9
+ var _ensureTestRunnerArguments = _interopRequireDefault(require("./rules/ensure-test-runner-arguments"));
10
+ var _ensureTestRunnerNestedCount = _interopRequireDefault(require("./rules/ensure-test-runner-nested-count"));
9
11
  var _noInvalidFeatureFlagUsage = _interopRequireDefault(require("./rules/no-invalid-feature-flag-usage"));
10
12
  var rules = {
11
13
  'ensure-feature-flag-registration': _ensureFeatureFlagRegistration.default,
14
+ 'ensure-test-runner-arguments': _ensureTestRunnerArguments.default,
15
+ 'ensure-test-runner-nested-count': _ensureTestRunnerNestedCount.default,
12
16
  'no-invalid-feature-flag-usage': _noInvalidFeatureFlagUsage.default
13
17
  };
14
18
  exports.rules = rules;
@@ -17,6 +21,8 @@ var configs = {
17
21
  plugins: ['@atlaskit/platform'],
18
22
  rules: {
19
23
  '@atlaskit/platform/ensure-feature-flag-registration': 'error',
24
+ '@atlaskit/platform/ensure-test-runner-arguments': 'error',
25
+ '@atlaskit/platform/ensure-test-runner-nested-count': 'warn',
20
26
  '@atlaskit/platform/no-invalid-feature-flag-usage': 'error'
21
27
  }
22
28
  }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var TEST_RUNNER_IDENTIFIER = 'ffTest';
10
+ var rule = {
11
+ meta: {
12
+ docs: {
13
+ recommended: false
14
+ },
15
+ type: 'problem',
16
+ fixable: 'code',
17
+ messages: {
18
+ onlyInlineFeatureFlag: 'Only pass in feature flag as string literal, please replace {{identifierName}} with its value.',
19
+ onlyInlineTestFunction: 'Only pass in test functions/cases in an inline manner. Test functions/cases should be passed in directly, instead of as variables. Please replace {{identifierName}} with its own definition.',
20
+ passDownExistingFeatureFlagParam: 'An argument symbolising existing FFs needs to be passed down as param when calling nested test runner. See examples in the package which declares this function.',
21
+ passDownExistingFeatureFlagArgument: 'An argument symbolising existing FFs needs to be passed in as argument when calling nested test runner. See examples in the package which declares this function.',
22
+ passDownExistingFeatureFlagNamesMatch: 'Argument names not matching when passing down existing feature flags. See examples in the package which declares this function.'
23
+ }
24
+ },
25
+ create: function create(context) {
26
+ return (0, _defineProperty2.default)({}, "CallExpression[callee.name=/".concat(TEST_RUNNER_IDENTIFIER, "/]"), function CallExpressionCalleeName(node) {
27
+ if (node.type === 'CallExpression') {
28
+ var _node$parent, _node$parent2;
29
+ var args = node.arguments;
30
+
31
+ // Verify FF is passed inline
32
+ if (args[0] && args[0].type !== 'Literal') {
33
+ return context.report({
34
+ node: node,
35
+ messageId: 'onlyInlineFeatureFlag',
36
+ data: {
37
+ identifierName: args[0].type === 'Identifier' ? args[0].name : ''
38
+ }
39
+ });
40
+ }
41
+
42
+ // Verify test functions/cases are passed inline
43
+ if (args[1] && args[1].type !== 'ArrowFunctionExpression') {
44
+ return context.report({
45
+ node: node,
46
+ messageId: 'onlyInlineTestFunction',
47
+ data: {
48
+ identifierName: args[1].type === 'Identifier' ? args[1].name : ''
49
+ }
50
+ });
51
+ }
52
+ if (args[2] && args[2].type !== 'ArrowFunctionExpression') {
53
+ return context.report({
54
+ node: node,
55
+ messageId: 'onlyInlineTestFunction',
56
+ data: {
57
+ identifierName: args[2].type === 'Identifier' ? args[2].name : ''
58
+ }
59
+ });
60
+ }
61
+
62
+ // Verify existing ff overrides are passed down if test runner is nested
63
+ var upperTestRunner = (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.parent;
64
+ if ((upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.type) === 'CallExpression' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.type) === 'Identifier' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.name) === TEST_RUNNER_IDENTIFIER && ((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === 'ArrowFunctionExpression') {
65
+ // Not pass in ff to the function that calls test runner
66
+ if (!node.parent.params[0] || node.parent.params[0].type !== 'Identifier') {
67
+ return context.report({
68
+ node: node.parent,
69
+ messageId: 'passDownExistingFeatureFlagParam',
70
+ fix: function fix(fixer) {
71
+ var parentNodeRange = node.parent.range;
72
+ return [fixer.replaceTextRange([parentNodeRange[0], parentNodeRange[0] + 2], 'ff'), node.arguments[3] ? fixer.replaceText(node.arguments[3], 'ff') : fixer.insertTextAfter(node.arguments[2], ', ff')];
73
+ }
74
+ });
75
+ }
76
+
77
+ // Not pass in ff to test runner as 4th argument
78
+ var paramName = node.parent.params[0].name;
79
+ if (!node.arguments[3] || node.arguments[3].type !== 'Identifier') {
80
+ return context.report({
81
+ node: node,
82
+ messageId: 'passDownExistingFeatureFlagArgument',
83
+ fix: function fix(fixer) {
84
+ return node.arguments[3] ? fixer.replaceText(node.arguments[3], paramName) : fixer.insertTextAfter(node.arguments[2], ", ".concat(paramName));
85
+ }
86
+ });
87
+ }
88
+ // Pass in the above two, but names don't match
89
+ var arguName = node.arguments[3].name;
90
+ if (paramName !== arguName) {
91
+ return context.report({
92
+ node: node.parent,
93
+ messageId: 'passDownExistingFeatureFlagNamesMatch',
94
+ fix: function fix(fixer) {
95
+ return fixer.replaceText(node.arguments[3], paramName);
96
+ }
97
+ });
98
+ }
99
+ }
100
+ }
101
+ return {};
102
+ });
103
+ }
104
+ };
105
+ var _default = rule;
106
+ exports.default = _default;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
10
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
11
+ var NESTED_LIMIT = 4;
12
+ var TEST_RUNNER_IDENTIFIER = 'ffTest';
13
+ var getDepthOfNestedRunner = function getDepthOfNestedRunner(node) {
14
+ // Calculate the depth of a binary tree, using a queue to track path
15
+ var queue = [];
16
+ queue.push(node);
17
+ var depth = 0;
18
+ while (queue.length > 0) {
19
+ var nodeCount = queue.length;
20
+ while (nodeCount > 0) {
21
+ var _currentNode$argument;
22
+ var currentNode = queue.shift();
23
+ if (currentNode.arguments[1].type === 'ArrowFunctionExpression' && currentNode.arguments[1].body.type === 'CallExpression' && currentNode.arguments[1].body.callee.type === 'Identifier' && currentNode.arguments[1].body.callee.name === TEST_RUNNER_IDENTIFIER) {
24
+ queue.push(_objectSpread(_objectSpread({}, currentNode.arguments[1].body), {}, {
25
+ parent: currentNode.parent
26
+ }));
27
+ }
28
+ if (((_currentNode$argument = currentNode.arguments[2]) === null || _currentNode$argument === void 0 ? void 0 : _currentNode$argument.type) === 'ArrowFunctionExpression' && currentNode.arguments[2].body.type === 'CallExpression' && currentNode.arguments[2].body.callee.type === 'Identifier' && currentNode.arguments[2].body.callee.name === TEST_RUNNER_IDENTIFIER) {
29
+ queue.push(_objectSpread(_objectSpread({}, currentNode.arguments[2].body), {}, {
30
+ parent: currentNode.parent
31
+ }));
32
+ }
33
+ nodeCount--;
34
+ }
35
+ depth++;
36
+ }
37
+ return depth;
38
+ };
39
+ var rule = {
40
+ meta: {
41
+ docs: {
42
+ recommended: false
43
+ },
44
+ type: 'problem',
45
+ messages: {
46
+ tooManyNestedTestRunner: '{{nestedTestRunner}} test runners are nested. Feature flags may need a clean-up'
47
+ }
48
+ },
49
+ create: function create(context) {
50
+ return (0, _defineProperty2.default)({}, "Program > * > CallExpression[callee.name=/".concat(TEST_RUNNER_IDENTIFIER, "/], CallExpression[callee.name=/describe/] > * > * > * > CallExpression[callee.name=/").concat(TEST_RUNNER_IDENTIFIER, "/]"), function ProgramCallExpressionCalleeNameCallExpressionCalleeNameDescribeCallExpressionCalleeName(node) {
51
+ if (node.type === 'CallExpression') {
52
+ // Calculate the depth of nested test runners, counting from the most outside
53
+ var depth = getDepthOfNestedRunner(node);
54
+ if (depth > NESTED_LIMIT) {
55
+ return context.report({
56
+ node: node,
57
+ messageId: 'tooManyNestedTestRunner',
58
+ data: {
59
+ nestedTestRunner: depth.toString()
60
+ }
61
+ });
62
+ }
63
+ }
64
+ return {};
65
+ });
66
+ }
67
+ };
68
+ var _default = rule;
69
+ exports.default = _default;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-platform",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "sideEffects": false
5
5
  }
@@ -1,7 +1,11 @@
1
1
  import ensureFeatureFlagRegistration from './rules/ensure-feature-flag-registration';
2
+ import ensureTestRunnerArguments from './rules/ensure-test-runner-arguments';
3
+ import ensureTestRunnerNestedCount from './rules/ensure-test-runner-nested-count';
2
4
  import noInvalidFeatureFlagUsage from './rules/no-invalid-feature-flag-usage';
3
5
  export const rules = {
4
6
  'ensure-feature-flag-registration': ensureFeatureFlagRegistration,
7
+ 'ensure-test-runner-arguments': ensureTestRunnerArguments,
8
+ 'ensure-test-runner-nested-count': ensureTestRunnerNestedCount,
5
9
  'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage
6
10
  };
7
11
  export const configs = {
@@ -9,6 +13,8 @@ export const configs = {
9
13
  plugins: ['@atlaskit/platform'],
10
14
  rules: {
11
15
  '@atlaskit/platform/ensure-feature-flag-registration': 'error',
16
+ '@atlaskit/platform/ensure-test-runner-arguments': 'error',
17
+ '@atlaskit/platform/ensure-test-runner-nested-count': 'warn',
12
18
  '@atlaskit/platform/no-invalid-feature-flag-usage': 'error'
13
19
  }
14
20
  }
@@ -0,0 +1,99 @@
1
+ const TEST_RUNNER_IDENTIFIER = 'ffTest';
2
+ const rule = {
3
+ meta: {
4
+ docs: {
5
+ recommended: false
6
+ },
7
+ type: 'problem',
8
+ fixable: 'code',
9
+ messages: {
10
+ onlyInlineFeatureFlag: 'Only pass in feature flag as string literal, please replace {{identifierName}} with its value.',
11
+ onlyInlineTestFunction: 'Only pass in test functions/cases in an inline manner. Test functions/cases should be passed in directly, instead of as variables. Please replace {{identifierName}} with its own definition.',
12
+ passDownExistingFeatureFlagParam: 'An argument symbolising existing FFs needs to be passed down as param when calling nested test runner. See examples in the package which declares this function.',
13
+ passDownExistingFeatureFlagArgument: 'An argument symbolising existing FFs needs to be passed in as argument when calling nested test runner. See examples in the package which declares this function.',
14
+ passDownExistingFeatureFlagNamesMatch: 'Argument names not matching when passing down existing feature flags. See examples in the package which declares this function.'
15
+ }
16
+ },
17
+ create(context) {
18
+ return {
19
+ [`CallExpression[callee.name=/${TEST_RUNNER_IDENTIFIER}/]`]: node => {
20
+ if (node.type === 'CallExpression') {
21
+ var _node$parent, _node$parent2;
22
+ const args = node.arguments;
23
+
24
+ // Verify FF is passed inline
25
+ if (args[0] && args[0].type !== 'Literal') {
26
+ return context.report({
27
+ node,
28
+ messageId: 'onlyInlineFeatureFlag',
29
+ data: {
30
+ identifierName: args[0].type === 'Identifier' ? args[0].name : ''
31
+ }
32
+ });
33
+ }
34
+
35
+ // Verify test functions/cases are passed inline
36
+ if (args[1] && args[1].type !== 'ArrowFunctionExpression') {
37
+ return context.report({
38
+ node,
39
+ messageId: 'onlyInlineTestFunction',
40
+ data: {
41
+ identifierName: args[1].type === 'Identifier' ? args[1].name : ''
42
+ }
43
+ });
44
+ }
45
+ if (args[2] && args[2].type !== 'ArrowFunctionExpression') {
46
+ return context.report({
47
+ node,
48
+ messageId: 'onlyInlineTestFunction',
49
+ data: {
50
+ identifierName: args[2].type === 'Identifier' ? args[2].name : ''
51
+ }
52
+ });
53
+ }
54
+
55
+ // Verify existing ff overrides are passed down if test runner is nested
56
+ let upperTestRunner = (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.parent;
57
+ if ((upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.type) === 'CallExpression' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.type) === 'Identifier' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.name) === TEST_RUNNER_IDENTIFIER && ((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === 'ArrowFunctionExpression') {
58
+ // Not pass in ff to the function that calls test runner
59
+ if (!node.parent.params[0] || node.parent.params[0].type !== 'Identifier') {
60
+ return context.report({
61
+ node: node.parent,
62
+ messageId: 'passDownExistingFeatureFlagParam',
63
+ fix: function (fixer) {
64
+ const parentNodeRange = node.parent.range;
65
+ return [fixer.replaceTextRange([parentNodeRange[0], parentNodeRange[0] + 2], 'ff'), node.arguments[3] ? fixer.replaceText(node.arguments[3], 'ff') : fixer.insertTextAfter(node.arguments[2], ', ff')];
66
+ }
67
+ });
68
+ }
69
+
70
+ // Not pass in ff to test runner as 4th argument
71
+ const paramName = node.parent.params[0].name;
72
+ if (!node.arguments[3] || node.arguments[3].type !== 'Identifier') {
73
+ return context.report({
74
+ node,
75
+ messageId: 'passDownExistingFeatureFlagArgument',
76
+ fix: function (fixer) {
77
+ return node.arguments[3] ? fixer.replaceText(node.arguments[3], paramName) : fixer.insertTextAfter(node.arguments[2], `, ${paramName}`);
78
+ }
79
+ });
80
+ }
81
+ // Pass in the above two, but names don't match
82
+ const arguName = node.arguments[3].name;
83
+ if (paramName !== arguName) {
84
+ return context.report({
85
+ node: node.parent,
86
+ messageId: 'passDownExistingFeatureFlagNamesMatch',
87
+ fix: function (fixer) {
88
+ return fixer.replaceText(node.arguments[3], paramName);
89
+ }
90
+ });
91
+ }
92
+ }
93
+ }
94
+ return {};
95
+ }
96
+ };
97
+ }
98
+ };
99
+ export default rule;
@@ -0,0 +1,63 @@
1
+ const NESTED_LIMIT = 4;
2
+ const TEST_RUNNER_IDENTIFIER = 'ffTest';
3
+ const getDepthOfNestedRunner = node => {
4
+ // Calculate the depth of a binary tree, using a queue to track path
5
+ let queue = [];
6
+ queue.push(node);
7
+ let depth = 0;
8
+ while (queue.length > 0) {
9
+ let nodeCount = queue.length;
10
+ while (nodeCount > 0) {
11
+ var _currentNode$argument;
12
+ let currentNode = queue.shift();
13
+ if (currentNode.arguments[1].type === 'ArrowFunctionExpression' && currentNode.arguments[1].body.type === 'CallExpression' && currentNode.arguments[1].body.callee.type === 'Identifier' && currentNode.arguments[1].body.callee.name === TEST_RUNNER_IDENTIFIER) {
14
+ queue.push({
15
+ ...currentNode.arguments[1].body,
16
+ parent: currentNode.parent
17
+ });
18
+ }
19
+ if (((_currentNode$argument = currentNode.arguments[2]) === null || _currentNode$argument === void 0 ? void 0 : _currentNode$argument.type) === 'ArrowFunctionExpression' && currentNode.arguments[2].body.type === 'CallExpression' && currentNode.arguments[2].body.callee.type === 'Identifier' && currentNode.arguments[2].body.callee.name === TEST_RUNNER_IDENTIFIER) {
20
+ queue.push({
21
+ ...currentNode.arguments[2].body,
22
+ parent: currentNode.parent
23
+ });
24
+ }
25
+ nodeCount--;
26
+ }
27
+ depth++;
28
+ }
29
+ return depth;
30
+ };
31
+ const rule = {
32
+ meta: {
33
+ docs: {
34
+ recommended: false
35
+ },
36
+ type: 'problem',
37
+ messages: {
38
+ tooManyNestedTestRunner: '{{nestedTestRunner}} test runners are nested. Feature flags may need a clean-up'
39
+ }
40
+ },
41
+ create(context) {
42
+ return {
43
+ // Find the most outside test runner, could be inside a describe or not
44
+ [`Program > * > CallExpression[callee.name=/${TEST_RUNNER_IDENTIFIER}/], CallExpression[callee.name=/describe/] > * > * > * > CallExpression[callee.name=/${TEST_RUNNER_IDENTIFIER}/]`]: node => {
45
+ if (node.type === 'CallExpression') {
46
+ // Calculate the depth of nested test runners, counting from the most outside
47
+ const depth = getDepthOfNestedRunner(node);
48
+ if (depth > NESTED_LIMIT) {
49
+ return context.report({
50
+ node,
51
+ messageId: 'tooManyNestedTestRunner',
52
+ data: {
53
+ nestedTestRunner: depth.toString()
54
+ }
55
+ });
56
+ }
57
+ }
58
+ return {};
59
+ }
60
+ };
61
+ }
62
+ };
63
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-platform",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "sideEffects": false
5
5
  }
package/dist/esm/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import ensureFeatureFlagRegistration from './rules/ensure-feature-flag-registration';
2
+ import ensureTestRunnerArguments from './rules/ensure-test-runner-arguments';
3
+ import ensureTestRunnerNestedCount from './rules/ensure-test-runner-nested-count';
2
4
  import noInvalidFeatureFlagUsage from './rules/no-invalid-feature-flag-usage';
3
5
  export var rules = {
4
6
  'ensure-feature-flag-registration': ensureFeatureFlagRegistration,
7
+ 'ensure-test-runner-arguments': ensureTestRunnerArguments,
8
+ 'ensure-test-runner-nested-count': ensureTestRunnerNestedCount,
5
9
  'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage
6
10
  };
7
11
  export var configs = {
@@ -9,6 +13,8 @@ export var configs = {
9
13
  plugins: ['@atlaskit/platform'],
10
14
  rules: {
11
15
  '@atlaskit/platform/ensure-feature-flag-registration': 'error',
16
+ '@atlaskit/platform/ensure-test-runner-arguments': 'error',
17
+ '@atlaskit/platform/ensure-test-runner-nested-count': 'warn',
12
18
  '@atlaskit/platform/no-invalid-feature-flag-usage': 'error'
13
19
  }
14
20
  }
@@ -0,0 +1,98 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ var TEST_RUNNER_IDENTIFIER = 'ffTest';
3
+ var rule = {
4
+ meta: {
5
+ docs: {
6
+ recommended: false
7
+ },
8
+ type: 'problem',
9
+ fixable: 'code',
10
+ messages: {
11
+ onlyInlineFeatureFlag: 'Only pass in feature flag as string literal, please replace {{identifierName}} with its value.',
12
+ onlyInlineTestFunction: 'Only pass in test functions/cases in an inline manner. Test functions/cases should be passed in directly, instead of as variables. Please replace {{identifierName}} with its own definition.',
13
+ passDownExistingFeatureFlagParam: 'An argument symbolising existing FFs needs to be passed down as param when calling nested test runner. See examples in the package which declares this function.',
14
+ passDownExistingFeatureFlagArgument: 'An argument symbolising existing FFs needs to be passed in as argument when calling nested test runner. See examples in the package which declares this function.',
15
+ passDownExistingFeatureFlagNamesMatch: 'Argument names not matching when passing down existing feature flags. See examples in the package which declares this function.'
16
+ }
17
+ },
18
+ create: function create(context) {
19
+ return _defineProperty({}, "CallExpression[callee.name=/".concat(TEST_RUNNER_IDENTIFIER, "/]"), function CallExpressionCalleeName(node) {
20
+ if (node.type === 'CallExpression') {
21
+ var _node$parent, _node$parent2;
22
+ var args = node.arguments;
23
+
24
+ // Verify FF is passed inline
25
+ if (args[0] && args[0].type !== 'Literal') {
26
+ return context.report({
27
+ node: node,
28
+ messageId: 'onlyInlineFeatureFlag',
29
+ data: {
30
+ identifierName: args[0].type === 'Identifier' ? args[0].name : ''
31
+ }
32
+ });
33
+ }
34
+
35
+ // Verify test functions/cases are passed inline
36
+ if (args[1] && args[1].type !== 'ArrowFunctionExpression') {
37
+ return context.report({
38
+ node: node,
39
+ messageId: 'onlyInlineTestFunction',
40
+ data: {
41
+ identifierName: args[1].type === 'Identifier' ? args[1].name : ''
42
+ }
43
+ });
44
+ }
45
+ if (args[2] && args[2].type !== 'ArrowFunctionExpression') {
46
+ return context.report({
47
+ node: node,
48
+ messageId: 'onlyInlineTestFunction',
49
+ data: {
50
+ identifierName: args[2].type === 'Identifier' ? args[2].name : ''
51
+ }
52
+ });
53
+ }
54
+
55
+ // Verify existing ff overrides are passed down if test runner is nested
56
+ var upperTestRunner = (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.parent;
57
+ if ((upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.type) === 'CallExpression' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.type) === 'Identifier' && (upperTestRunner === null || upperTestRunner === void 0 ? void 0 : upperTestRunner.callee.name) === TEST_RUNNER_IDENTIFIER && ((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === 'ArrowFunctionExpression') {
58
+ // Not pass in ff to the function that calls test runner
59
+ if (!node.parent.params[0] || node.parent.params[0].type !== 'Identifier') {
60
+ return context.report({
61
+ node: node.parent,
62
+ messageId: 'passDownExistingFeatureFlagParam',
63
+ fix: function fix(fixer) {
64
+ var parentNodeRange = node.parent.range;
65
+ return [fixer.replaceTextRange([parentNodeRange[0], parentNodeRange[0] + 2], 'ff'), node.arguments[3] ? fixer.replaceText(node.arguments[3], 'ff') : fixer.insertTextAfter(node.arguments[2], ', ff')];
66
+ }
67
+ });
68
+ }
69
+
70
+ // Not pass in ff to test runner as 4th argument
71
+ var paramName = node.parent.params[0].name;
72
+ if (!node.arguments[3] || node.arguments[3].type !== 'Identifier') {
73
+ return context.report({
74
+ node: node,
75
+ messageId: 'passDownExistingFeatureFlagArgument',
76
+ fix: function fix(fixer) {
77
+ return node.arguments[3] ? fixer.replaceText(node.arguments[3], paramName) : fixer.insertTextAfter(node.arguments[2], ", ".concat(paramName));
78
+ }
79
+ });
80
+ }
81
+ // Pass in the above two, but names don't match
82
+ var arguName = node.arguments[3].name;
83
+ if (paramName !== arguName) {
84
+ return context.report({
85
+ node: node.parent,
86
+ messageId: 'passDownExistingFeatureFlagNamesMatch',
87
+ fix: function fix(fixer) {
88
+ return fixer.replaceText(node.arguments[3], paramName);
89
+ }
90
+ });
91
+ }
92
+ }
93
+ }
94
+ return {};
95
+ });
96
+ }
97
+ };
98
+ export default rule;
@@ -0,0 +1,61 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
4
+ var NESTED_LIMIT = 4;
5
+ var TEST_RUNNER_IDENTIFIER = 'ffTest';
6
+ var getDepthOfNestedRunner = function getDepthOfNestedRunner(node) {
7
+ // Calculate the depth of a binary tree, using a queue to track path
8
+ var queue = [];
9
+ queue.push(node);
10
+ var depth = 0;
11
+ while (queue.length > 0) {
12
+ var nodeCount = queue.length;
13
+ while (nodeCount > 0) {
14
+ var _currentNode$argument;
15
+ var currentNode = queue.shift();
16
+ if (currentNode.arguments[1].type === 'ArrowFunctionExpression' && currentNode.arguments[1].body.type === 'CallExpression' && currentNode.arguments[1].body.callee.type === 'Identifier' && currentNode.arguments[1].body.callee.name === TEST_RUNNER_IDENTIFIER) {
17
+ queue.push(_objectSpread(_objectSpread({}, currentNode.arguments[1].body), {}, {
18
+ parent: currentNode.parent
19
+ }));
20
+ }
21
+ if (((_currentNode$argument = currentNode.arguments[2]) === null || _currentNode$argument === void 0 ? void 0 : _currentNode$argument.type) === 'ArrowFunctionExpression' && currentNode.arguments[2].body.type === 'CallExpression' && currentNode.arguments[2].body.callee.type === 'Identifier' && currentNode.arguments[2].body.callee.name === TEST_RUNNER_IDENTIFIER) {
22
+ queue.push(_objectSpread(_objectSpread({}, currentNode.arguments[2].body), {}, {
23
+ parent: currentNode.parent
24
+ }));
25
+ }
26
+ nodeCount--;
27
+ }
28
+ depth++;
29
+ }
30
+ return depth;
31
+ };
32
+ var rule = {
33
+ meta: {
34
+ docs: {
35
+ recommended: false
36
+ },
37
+ type: 'problem',
38
+ messages: {
39
+ tooManyNestedTestRunner: '{{nestedTestRunner}} test runners are nested. Feature flags may need a clean-up'
40
+ }
41
+ },
42
+ create: function create(context) {
43
+ return _defineProperty({}, "Program > * > CallExpression[callee.name=/".concat(TEST_RUNNER_IDENTIFIER, "/], CallExpression[callee.name=/describe/] > * > * > * > CallExpression[callee.name=/").concat(TEST_RUNNER_IDENTIFIER, "/]"), function ProgramCallExpressionCalleeNameCallExpressionCalleeNameDescribeCallExpressionCalleeName(node) {
44
+ if (node.type === 'CallExpression') {
45
+ // Calculate the depth of nested test runners, counting from the most outside
46
+ var depth = getDepthOfNestedRunner(node);
47
+ if (depth > NESTED_LIMIT) {
48
+ return context.report({
49
+ node: node,
50
+ messageId: 'tooManyNestedTestRunner',
51
+ data: {
52
+ nestedTestRunner: depth.toString()
53
+ }
54
+ });
55
+ }
56
+ }
57
+ return {};
58
+ });
59
+ }
60
+ };
61
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-platform",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "sideEffects": false
5
5
  }
@@ -1,5 +1,7 @@
1
1
  export declare const rules: {
2
2
  'ensure-feature-flag-registration': import("eslint").Rule.RuleModule;
3
+ 'ensure-test-runner-arguments': import("eslint").Rule.RuleModule;
4
+ 'ensure-test-runner-nested-count': import("eslint").Rule.RuleModule;
3
5
  'no-invalid-feature-flag-usage': import("eslint").Rule.RuleModule;
4
6
  };
5
7
  export declare const configs: {
@@ -7,6 +9,8 @@ export declare const configs: {
7
9
  plugins: string[];
8
10
  rules: {
9
11
  '@atlaskit/platform/ensure-feature-flag-registration': string;
12
+ '@atlaskit/platform/ensure-test-runner-arguments': string;
13
+ '@atlaskit/platform/ensure-test-runner-nested-count': string;
10
14
  '@atlaskit/platform/no-invalid-feature-flag-usage': string;
11
15
  };
12
16
  };
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-platform",
3
3
  "description": "The essential plugin for use with Atlassian frontend platform tools",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "atlassian": {
7
7
  "team": "UIP - Platform Integration Trust (PITa)",