@atlaskit/eslint-plugin-platform 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/afm-jira/tsconfig.json +5 -1
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/rules/compiled/expand-background-shorthand/index.js +5 -5
  5. package/dist/cjs/rules/feature-gating/inline-usage/index.js +1 -1
  6. package/dist/cjs/rules/feature-gating/no-alias/index.js +1 -1
  7. package/dist/cjs/rules/feature-gating/no-module-level-eval/index.js +4 -3
  8. package/dist/cjs/rules/feature-gating/no-module-level-eval-nav4/index.js +4 -3
  9. package/dist/cjs/rules/feature-gating/no-preconditioning/index.js +1 -1
  10. package/dist/cjs/rules/feature-gating/prefer-fg/index.js +7 -6
  11. package/dist/cjs/rules/feature-gating/static-feature-flags/index.js +2 -2
  12. package/dist/cjs/rules/feature-gating/use-recommended-utils/index.js +2 -2
  13. package/dist/cjs/rules/feature-gating/utils.js +10 -9
  14. package/dist/cjs/rules/util/context-compat.js +33 -0
  15. package/dist/es2019/index.js +1 -1
  16. package/dist/es2019/rules/compiled/expand-background-shorthand/index.js +4 -4
  17. package/dist/es2019/rules/feature-gating/inline-usage/index.js +1 -1
  18. package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
  19. package/dist/es2019/rules/feature-gating/no-module-level-eval/index.js +4 -3
  20. package/dist/es2019/rules/feature-gating/no-module-level-eval-nav4/index.js +4 -3
  21. package/dist/es2019/rules/feature-gating/no-preconditioning/index.js +1 -1
  22. package/dist/es2019/rules/feature-gating/prefer-fg/index.js +5 -4
  23. package/dist/es2019/rules/feature-gating/static-feature-flags/index.js +2 -2
  24. package/dist/es2019/rules/feature-gating/use-recommended-utils/index.js +2 -2
  25. package/dist/es2019/rules/feature-gating/utils.js +9 -8
  26. package/dist/es2019/rules/util/context-compat.js +27 -0
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/rules/compiled/expand-background-shorthand/index.js +5 -5
  29. package/dist/esm/rules/feature-gating/inline-usage/index.js +1 -1
  30. package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
  31. package/dist/esm/rules/feature-gating/no-module-level-eval/index.js +4 -3
  32. package/dist/esm/rules/feature-gating/no-module-level-eval-nav4/index.js +4 -3
  33. package/dist/esm/rules/feature-gating/no-preconditioning/index.js +1 -1
  34. package/dist/esm/rules/feature-gating/prefer-fg/index.js +7 -6
  35. package/dist/esm/rules/feature-gating/static-feature-flags/index.js +2 -2
  36. package/dist/esm/rules/feature-gating/use-recommended-utils/index.js +2 -2
  37. package/dist/esm/rules/feature-gating/utils.js +10 -9
  38. package/dist/esm/rules/util/context-compat.js +27 -0
  39. package/dist/types/rules/feature-gating/utils.d.ts +4 -3
  40. package/dist/types/rules/util/context-compat.d.ts +10 -0
  41. package/dist/types-ts4.5/rules/feature-gating/utils.d.ts +4 -3
  42. package/dist/types-ts4.5/rules/util/context-compat.d.ts +10 -0
  43. package/package.json +2 -5
  44. package/src/index.tsx +1 -1
  45. package/src/rules/compiled/expand-background-shorthand/__tests__/rule.test.ts +1 -1
  46. package/src/rules/compiled/expand-background-shorthand/index.tsx +4 -4
  47. package/src/rules/feature-gating/inline-usage/index.tsx +1 -1
  48. package/src/rules/feature-gating/no-alias/index.tsx +6 -1
  49. package/src/rules/feature-gating/no-module-level-eval/index.tsx +8 -6
  50. package/src/rules/feature-gating/no-module-level-eval-nav4/index.tsx +6 -5
  51. package/src/rules/feature-gating/no-preconditioning/index.tsx +1 -1
  52. package/src/rules/feature-gating/prefer-fg/index.tsx +8 -6
  53. package/src/rules/feature-gating/static-feature-flags/index.tsx +2 -2
  54. package/src/rules/feature-gating/use-recommended-utils/index.tsx +2 -2
  55. package/src/rules/feature-gating/utils.tsx +8 -6
  56. package/src/rules/util/context-compat.ts +28 -0
  57. package/tsconfig.app.json +3 -0
@@ -1,6 +1,7 @@
1
1
  import { isAPIimport } from '../utils';
2
- var isInFunctionLevel = function isInFunctionLevel(context) {
3
- var scope = context.getScope();
2
+ import { getScope } from '../../util/context-compat';
3
+ var isInFunctionLevel = function isInFunctionLevel(context, node) {
4
+ var scope = getScope(context, node);
4
5
  while (((_scope = scope) === null || _scope === void 0 ? void 0 : _scope.type) !== 'module' && ((_scope2 = scope) === null || _scope2 === void 0 ? void 0 : _scope2.type) !== 'global') {
5
6
  var _scope, _scope2;
6
7
  if (scope.type === 'function') {
@@ -26,7 +27,7 @@ var rule = {
26
27
  create: function create(context) {
27
28
  return {
28
29
  'CallExpression[callee.type="Identifier"]': function CallExpressionCalleeTypeIdentifier(node) {
29
- if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && isAPIimport(node.callee.name, context) && !isInFunctionLevel(context)) {
30
+ if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && isAPIimport(node.callee.name, context, node) && !isInFunctionLevel(context, node)) {
30
31
  context.report({
31
32
  messageId: 'noModuleLevelEval',
32
33
  node: node
@@ -1,3 +1,4 @@
1
+ import { getScope } from '../../util/context-compat';
1
2
  var featureLibraryFunctions = new Set([
2
3
  /*
3
4
  * STOP!
@@ -10,8 +11,8 @@ var featureLibraryFunctions = new Set([
10
11
  * Slack thread: https://atlassian.slack.com/archives/CFGLH1ZS8/p1726449739284819
11
12
  */
12
13
  'isVisualRefreshEnabled', 'getMetaBoolean', 'getNav4Rollout', 'getWillShowNav3', 'getWillShowNav4', 'getWillShowNav4UserOptIn', 'getWillShowNav4UserOptOut']);
13
- var isInFunctionLevel = function isInFunctionLevel(context) {
14
- var scope = context.getScope();
14
+ var isInFunctionLevel = function isInFunctionLevel(context, node) {
15
+ var scope = getScope(context, node);
15
16
  while (((_scope = scope) === null || _scope === void 0 ? void 0 : _scope.type) !== 'module' && ((_scope2 = scope) === null || _scope2 === void 0 ? void 0 : _scope2.type) !== 'global') {
16
17
  var _scope, _scope2;
17
18
  if (scope.type === 'function') {
@@ -37,7 +38,7 @@ var rule = {
37
38
  create: function create(context) {
38
39
  return {
39
40
  'CallExpression[callee.type="Identifier"]': function CallExpressionCalleeTypeIdentifier(node) {
40
- if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && featureLibraryFunctions.has(node.callee.name) && !isInFunctionLevel(context)) {
41
+ if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && featureLibraryFunctions.has(node.callee.name) && !isInFunctionLevel(context, node)) {
41
42
  context.report({
42
43
  messageId: 'noModuleLevelEval',
43
44
  node: node
@@ -14,7 +14,7 @@ var getGateType = function getGateType(node, context) {
14
14
  var callee = node.callee;
15
15
  var isFeatureGate = type === 'CallExpression' && callee.type === 'Identifier' && (
16
16
  // Experiments cannot have other experiments as preconditions, only gates
17
- callee.name === 'fg' || isExpUsage(callee.name)) && isAPIimport(callee.name, context);
17
+ callee.name === 'fg' || isExpUsage(callee.name)) && isAPIimport(callee.name, context, node);
18
18
  return isFeatureGate ? callee.name : '';
19
19
  }
20
20
  return '';
@@ -4,11 +4,12 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
4
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
5
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
6
  import { FEATURE_API_IMPORT_SOURCES } from '../../constants';
7
+ import { getScope } from '../../util/context-compat';
7
8
  var validateUsage = function validateUsage(node, utilName, context, changeMap) {
8
- var _context$getScope$ref;
9
- var resolved = (_context$getScope$ref = context.getScope().references.find(function (ref) {
9
+ var _getScope$references$;
10
+ var resolved = (_getScope$references$ = getScope(context, node).references.find(function (ref) {
10
11
  return ref.identifier.name === utilName;
11
- })) === null || _context$getScope$ref === void 0 ? void 0 : _context$getScope$ref.resolved;
12
+ })) === null || _getScope$references$ === void 0 ? void 0 : _getScope$references$.resolved;
12
13
  var importSpecifierDefinition = resolved === null || resolved === void 0 ? void 0 : resolved.defs.find(function (def) {
13
14
  var _def$node, _def$parent;
14
15
  return ((_def$node = def.node) === null || _def$node === void 0 ? void 0 : _def$node.type) === 'ImportSpecifier' && FEATURE_API_IMPORT_SOURCES.has((_def$parent = def.parent) === null || _def$parent === void 0 ? void 0 : _def$parent.source.value);
@@ -65,12 +66,12 @@ var rule = {
65
66
  changeMap = changeMap || new Map();
66
67
  validateUsage(node, 'getBooleanFF', context, changeMap);
67
68
  },
68
- 'Program:exit': function ProgramExit() {
69
+ 'Program:exit': function ProgramExit(node) {
69
70
  var _changeMap;
70
71
  if ((_changeMap = changeMap) !== null && _changeMap !== void 0 && _changeMap.size) {
71
72
  changeMap.forEach(function (changeCounts, importDeclaration) {
72
- var _context$getScope$chi = _slicedToArray(context.getScope().childScopes, 1),
73
- moduleScope = _context$getScope$chi[0];
73
+ var _getScope$childScopes = _slicedToArray(getScope(context, node).childScopes, 1),
74
+ moduleScope = _getScope$childScopes[0];
74
75
  var importSpecifiers = new Set(importDeclaration.specifiers.map(function (_ref) {
75
76
  var imported = _ref.imported;
76
77
  return imported.name;
@@ -26,12 +26,12 @@ var rule = {
26
26
  if (node.type !== 'CallExpression') {
27
27
  return;
28
28
  }
29
- if (node.callee.type === 'Identifier' && (!targetedFunctionsSwitch.has(node.callee.name) || !isIdentifierImportedFrom(node.callee.name, IMPORT_SOURCES, context))) {
29
+ if (node.callee.type === 'Identifier' && (!targetedFunctionsSwitch.has(node.callee.name) || !isIdentifierImportedFrom(node.callee.name, IMPORT_SOURCES, context, node))) {
30
30
  return;
31
31
  }
32
32
  var nameArgument = node.arguments[0];
33
33
  if (nameArgument.type === 'Identifier') {
34
- var def = getDef(nameArgument.name, context);
34
+ var def = getDef(nameArgument.name, context, node);
35
35
  if (def != null && def.type === 'Variable') {
36
36
  var _ref = def.node.init,
37
37
  value = _ref.value;
@@ -15,7 +15,7 @@ var rule = {
15
15
  create: function create(context) {
16
16
  return {
17
17
  'CallExpression > MemberExpression:matches([property.name="checkGate"])': function CallExpressionMemberExpressionMatchesPropertyNameCheckGate(node) {
18
- if (node.object.type === 'Identifier' && isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context)) {
18
+ if (node.object.type === 'Identifier' && isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context, node)) {
19
19
  context.report({
20
20
  messageId: 'useRecommended',
21
21
  node: node,
@@ -28,7 +28,7 @@ var rule = {
28
28
  }
29
29
  },
30
30
  'CallExpression > MemberExpression:matches([property.name="getExperimentValue"])': function CallExpressionMemberExpressionMatchesPropertyNameGetExperimentValue(node) {
31
- if (node.object.type === 'Identifier' && isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context)) {
31
+ if (node.object.type === 'Identifier' && isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context, node)) {
32
32
  context.report({
33
33
  messageId: 'notSupported',
34
34
  node: node
@@ -2,25 +2,26 @@ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol
2
2
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
3
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
4
  import { FEATURE_API_IMPORT_SOURCES } from '../constants';
5
- export function isIdentifierImportedFrom(identifierName, sources, context) {
5
+ import { getScope } from '../util/context-compat';
6
+ export function isIdentifierImportedFrom(identifierName, sources, context, node) {
6
7
  if (sources.size > 0) {
7
- var _context$getScope$ref, _context$getScope$ref2;
8
- return (_context$getScope$ref = (_context$getScope$ref2 = context.getScope().references.find(function (ref) {
8
+ var _getScope$references$, _getScope$references$2;
9
+ return (_getScope$references$ = (_getScope$references$2 = getScope(context, node).references.find(function (ref) {
9
10
  return ref.identifier.name === identifierName;
10
- })) === null || _context$getScope$ref2 === void 0 || (_context$getScope$ref2 = _context$getScope$ref2.resolved) === null || _context$getScope$ref2 === void 0 ? void 0 : _context$getScope$ref2.defs.some(function (def) {
11
+ })) === null || _getScope$references$2 === void 0 || (_getScope$references$2 = _getScope$references$2.resolved) === null || _getScope$references$2 === void 0 ? void 0 : _getScope$references$2.defs.some(function (def) {
11
12
  var _def$parent;
12
13
  return ((_def$parent = def.parent) === null || _def$parent === void 0 ? void 0 : _def$parent.type) === 'ImportDeclaration' && sources.has(def.parent.source.value + '');
13
- })) !== null && _context$getScope$ref !== void 0 ? _context$getScope$ref : false;
14
+ })) !== null && _getScope$references$ !== void 0 ? _getScope$references$ : false;
14
15
  }
15
16
  return false;
16
17
  }
17
- export function isAPIimport(functionName, context) {
18
- return isIdentifierImportedFrom(functionName, FEATURE_API_IMPORT_SOURCES, context);
18
+ export function isAPIimport(functionName, context, node) {
19
+ return isIdentifierImportedFrom(functionName, FEATURE_API_IMPORT_SOURCES, context, node);
19
20
  }
20
21
 
21
22
  // returns the definition node of a variable if it's declared within the scope of the file
22
- export function getDef(name, context) {
23
- var scope = context.getScope();
23
+ export function getDef(name, context, node) {
24
+ var scope = getScope(context, node);
24
25
  while (scope && scope.type !== 'global') {
25
26
  var _iterator = _createForOfIteratorHelper(scope.variables),
26
27
  _step;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * TODO: Consider whether this should be replaced by ESLint's compat library.
3
+ * Either way, this should be removed once we no longer need to support ESLint versions less than 8.40.
4
+ */
5
+
6
+ /**
7
+ * A compatibility layer to support older versions of ESLint.
8
+ * `context.sourceCode` is the preferred way to access SourceCode, as
9
+ * `context.getSourceCode()` is deprecated in v8 and removed in v9.
10
+ * @param context - The ESLint rule context
11
+ */
12
+ var getSourceCode = function getSourceCode(context) {
13
+ var _context$sourceCode;
14
+ return (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode();
15
+ };
16
+
17
+ /**
18
+ * A compatibility layer to support older versions of ESLint.
19
+ * `context.sourceCode.getScope()` is the preferred way to access Scope, as
20
+ * `context.getScope()` was removed in v9.
21
+ * @param context - The ESLint rule context
22
+ * @param node - The node to get the scope for
23
+ */
24
+ export var getScope = function getScope(context, node) {
25
+ var _getSourceCode$getSco, _getSourceCode;
26
+ return (_getSourceCode$getSco = (_getSourceCode = getSourceCode(context)) === null || _getSourceCode === void 0 ? void 0 : _getSourceCode.getScope(node)) !== null && _getSourceCode$getSco !== void 0 ? _getSourceCode$getSco : context.getScope();
27
+ };
@@ -1,7 +1,8 @@
1
1
  import type { Rule, Scope } from 'eslint';
2
- export declare function isIdentifierImportedFrom(identifierName: string, sources: Set<string>, context: Rule.RuleContext): boolean;
3
- export declare function isAPIimport(functionName: string, context: Rule.RuleContext): boolean;
4
- export declare function getDef(name: string, context: Rule.RuleContext): Scope.Definition | null | undefined;
2
+ import type { Node as EstreeNode } from 'estree';
3
+ export declare function isIdentifierImportedFrom(identifierName: string, sources: Set<string>, context: Rule.RuleContext, node: EstreeNode): boolean;
4
+ export declare function isAPIimport(functionName: string, context: Rule.RuleContext, node: EstreeNode): boolean;
5
+ export declare function getDef(name: string, context: Rule.RuleContext, node: EstreeNode): Scope.Definition | null | undefined;
5
6
  export type Node<T extends Rule.Node['type']> = Extract<Rule.Node, {
6
7
  type: T;
7
8
  }>;
@@ -0,0 +1,10 @@
1
+ import type { Rule, Scope } from 'eslint';
2
+ import type { Node } from 'estree';
3
+ /**
4
+ * A compatibility layer to support older versions of ESLint.
5
+ * `context.sourceCode.getScope()` is the preferred way to access Scope, as
6
+ * `context.getScope()` was removed in v9.
7
+ * @param context - The ESLint rule context
8
+ * @param node - The node to get the scope for
9
+ */
10
+ export declare const getScope: (context: Rule.RuleContext, node: Node) => Scope.Scope;
@@ -1,7 +1,8 @@
1
1
  import type { Rule, Scope } from 'eslint';
2
- export declare function isIdentifierImportedFrom(identifierName: string, sources: Set<string>, context: Rule.RuleContext): boolean;
3
- export declare function isAPIimport(functionName: string, context: Rule.RuleContext): boolean;
4
- export declare function getDef(name: string, context: Rule.RuleContext): Scope.Definition | null | undefined;
2
+ import type { Node as EstreeNode } from 'estree';
3
+ export declare function isIdentifierImportedFrom(identifierName: string, sources: Set<string>, context: Rule.RuleContext, node: EstreeNode): boolean;
4
+ export declare function isAPIimport(functionName: string, context: Rule.RuleContext, node: EstreeNode): boolean;
5
+ export declare function getDef(name: string, context: Rule.RuleContext, node: EstreeNode): Scope.Definition | null | undefined;
5
6
  export type Node<T extends Rule.Node['type']> = Extract<Rule.Node, {
6
7
  type: T;
7
8
  }>;
@@ -0,0 +1,10 @@
1
+ import type { Rule, Scope } from 'eslint';
2
+ import type { Node } from 'estree';
3
+ /**
4
+ * A compatibility layer to support older versions of ESLint.
5
+ * `context.sourceCode.getScope()` is the preferred way to access Scope, as
6
+ * `context.getScope()` was removed in v9.
7
+ * @param context - The ESLint rule context
8
+ * @param node - The node to get the scope for
9
+ */
10
+ export declare const getScope: (context: Rule.RuleContext, node: Node) => Scope.Scope;
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": "1.0.0",
4
+ "version": "2.0.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "atlassian": {
7
7
  "team": "Build Infra",
@@ -33,18 +33,15 @@
33
33
  "dependencies": {
34
34
  "@atlaskit/eslint-utils": "^1.7.0",
35
35
  "@babel/runtime": "^7.0.0",
36
+ "@compiled/eslint-plugin": "^0.17.0",
36
37
  "@manypkg/find-root": "^1.1.0",
37
38
  "fuse.js": "^6.6.2",
38
39
  "read-pkg-up": "^7.0.1"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@atlassian/ts-loader": "*",
42
- "@compiled/eslint-plugin": "^0.17.0",
43
43
  "@types/eslint": "^8.56.6",
44
44
  "eslint": "^8.57.0",
45
45
  "outdent": "^0.5.0"
46
- },
47
- "peerDependencies": {
48
- "@compiled/eslint-plugin": "^0.17.0"
49
46
  }
50
47
  }
package/src/index.tsx CHANGED
@@ -63,7 +63,7 @@ const commonConfig = {
63
63
  '@atlaskit/platform/no-module-level-eval-nav4': 'error',
64
64
  // Compiled: rules that are not included via `@compiled/recommended
65
65
  '@atlaskit/platform/expand-border-shorthand': 'error',
66
- '@atlaskit/platform/expand-background-shorthand': 'warn',
66
+ '@atlaskit/platform/expand-background-shorthand': 'error',
67
67
  '@compiled/jsx-pragma': [
68
68
  'error',
69
69
  {
@@ -51,7 +51,7 @@ tester.run('expand-border-shorthand', expandBackgroundShorthand, {
51
51
  background: 'transparent'
52
52
  });
53
53
  const styles2 = css({
54
- background: url(image.png)
54
+ background: url('image.png')
55
55
  });
56
56
  const styles3 = css({
57
57
  background: 0
@@ -5,11 +5,12 @@ import {
5
5
  isCompiled,
6
6
  isAtlasKitCSS,
7
7
  } from '@atlaskit/eslint-utils/is-supported-import';
8
+ import { getScope } from '../../util/context-compat';
8
9
 
9
10
  // Checks if the function that holds the border property is using an import package that this rule is targeting
10
- const isCompiledAPI = (context: Rule.RuleContext): boolean => {
11
+ const isCompiledAPI = (context: Rule.RuleContext, node: Node): boolean => {
11
12
  const importSources = getImportSources(context);
12
- const { references } = context.getScope();
13
+ const { references } = getScope(context, node);
13
14
  const ancestors = context.getAncestors();
14
15
  if (
15
16
  ancestors.some(
@@ -49,7 +50,7 @@ export const expandBackgroundShorthand: Rule.RuleModule = {
49
50
  create(context) {
50
51
  return {
51
52
  'Property[key.name="background"]': function (node: Property) {
52
- if (isCompiledAPI(context) && isTokenCallExpression(node.value)) {
53
+ if (isCompiledAPI(context, node) && isTokenCallExpression(node.value)) {
53
54
  context.report({
54
55
  node,
55
56
  messageId: 'expandBackgroundShorthand',
@@ -57,7 +58,6 @@ export const expandBackgroundShorthand: Rule.RuleModule = {
57
58
  return fixer.replaceText(node.key, `backgroundColor`);
58
59
  },
59
60
  });
60
- return;
61
61
  }
62
62
  },
63
63
  };
@@ -27,7 +27,7 @@ const validateCallExpression = (
27
27
  const shouldWarn =
28
28
  callee.type === 'Identifier' &&
29
29
  targetedFunctionsSwitch.has(callee.name) &&
30
- isAPIimport(callee.name, context);
30
+ isAPIimport(callee.name, context, node);
31
31
 
32
32
  if (shouldWarn) {
33
33
  const defDeclaration = findDefinitionDeclaration(node.parent);
@@ -59,7 +59,12 @@ const rule: Rule.RuleModule = {
59
59
  return;
60
60
  }
61
61
 
62
- const isReassignment = isIdentifierImportedFrom(node.init.name, IMPORT_SOURCES, context);
62
+ const isReassignment = isIdentifierImportedFrom(
63
+ node.init.name,
64
+ IMPORT_SOURCES,
65
+ context,
66
+ node,
67
+ );
63
68
 
64
69
  if (isReassignment) {
65
70
  context.report({
@@ -1,8 +1,10 @@
1
1
  import type { Rule } from 'eslint';
2
- import { isAPIimport, type Node } from '../utils';
2
+ import type { Node } from 'estree';
3
+ import { isAPIimport } from '../utils';
4
+ import { getScope } from '../../util/context-compat';
3
5
 
4
- const isInFunctionLevel = (context: Rule.RuleContext) => {
5
- let scope: any = context.getScope();
6
+ const isInFunctionLevel = (context: Rule.RuleContext, node: Node) => {
7
+ let scope: any = getScope(context, node);
6
8
 
7
9
  while (scope?.type !== 'module' && scope?.type !== 'global') {
8
10
  if (scope.type === 'function') {
@@ -32,12 +34,12 @@ const rule: Rule.RuleModule = {
32
34
  },
33
35
  create(context) {
34
36
  return {
35
- 'CallExpression[callee.type="Identifier"]': (node: Node<any>) => {
37
+ 'CallExpression[callee.type="Identifier"]': (node: Node) => {
36
38
  if (
37
39
  node.type === 'CallExpression' &&
38
40
  node.callee.type === 'Identifier' &&
39
- isAPIimport(node.callee.name, context) &&
40
- !isInFunctionLevel(context)
41
+ isAPIimport(node.callee.name, context, node) &&
42
+ !isInFunctionLevel(context, node)
41
43
  ) {
42
44
  context.report({
43
45
  messageId: 'noModuleLevelEval',
@@ -1,5 +1,6 @@
1
1
  import type { Rule } from 'eslint';
2
- import { type Node } from '../utils';
2
+ import type { Node } from 'estree';
3
+ import { getScope } from '../../util/context-compat';
3
4
 
4
5
  const featureLibraryFunctions = new Set([
5
6
  /*
@@ -21,8 +22,8 @@ const featureLibraryFunctions = new Set([
21
22
  'getWillShowNav4UserOptOut',
22
23
  ]);
23
24
 
24
- const isInFunctionLevel = (context: Rule.RuleContext) => {
25
- let scope: any = context.getScope();
25
+ const isInFunctionLevel = (context: Rule.RuleContext, node: Node) => {
26
+ let scope: any = getScope(context, node);
26
27
 
27
28
  while (scope?.type !== 'module' && scope?.type !== 'global') {
28
29
  if (scope.type === 'function') {
@@ -52,12 +53,12 @@ const rule: Rule.RuleModule = {
52
53
  },
53
54
  create(context) {
54
55
  return {
55
- 'CallExpression[callee.type="Identifier"]': (node: Node<any>) => {
56
+ 'CallExpression[callee.type="Identifier"]': (node: Node) => {
56
57
  if (
57
58
  node.type === 'CallExpression' &&
58
59
  node.callee.type === 'Identifier' &&
59
60
  featureLibraryFunctions.has(node.callee.name) &&
60
- !isInFunctionLevel(context)
61
+ !isInFunctionLevel(context, node)
61
62
  ) {
62
63
  context.report({
63
64
  messageId: 'noModuleLevelEval',
@@ -24,7 +24,7 @@ const getGateType = (node: Rule.Node, context: Rule.RuleContext): string => {
24
24
  callee.type === 'Identifier' &&
25
25
  // Experiments cannot have other experiments as preconditions, only gates
26
26
  (callee.name === 'fg' || isExpUsage(callee.name)) &&
27
- isAPIimport(callee.name, context);
27
+ isAPIimport(callee.name, context, node);
28
28
 
29
29
  return isFeatureGate ? callee.name : '';
30
30
  }
@@ -1,7 +1,9 @@
1
1
  import type { Rule } from 'eslint';
2
2
 
3
3
  import { FEATURE_API_IMPORT_SOURCES } from '../../constants';
4
- import { type Node } from '../utils';
4
+ import type { Node } from '../utils';
5
+ import type { Program } from 'estree';
6
+ import { getScope } from '../../util/context-compat';
5
7
 
6
8
  const validateUsage = (
7
9
  node: Node<'CallExpression'>,
@@ -9,9 +11,9 @@ const validateUsage = (
9
11
  context: Rule.RuleContext,
10
12
  changeMap: Map<any, any>,
11
13
  ) => {
12
- const resolved = context
13
- .getScope()
14
- .references.find((ref) => ref.identifier.name === utilName)?.resolved;
14
+ const resolved = getScope(context, node).references.find(
15
+ (ref) => ref.identifier.name === utilName,
16
+ )?.resolved;
15
17
 
16
18
  const importSpecifierDefinition = resolved?.defs.find(
17
19
  (def: any) =>
@@ -69,10 +71,10 @@ const rule: Rule.RuleModule = {
69
71
  changeMap = changeMap || new Map();
70
72
  validateUsage(node, 'getBooleanFF', context, changeMap);
71
73
  },
72
- 'Program:exit': () => {
74
+ 'Program:exit': (node: Program) => {
73
75
  if (changeMap?.size) {
74
76
  changeMap.forEach((changeCounts, importDeclaration) => {
75
- const [moduleScope] = context.getScope().childScopes;
77
+ const [moduleScope] = getScope(context, node).childScopes;
76
78
  const importSpecifiers = new Set(
77
79
  importDeclaration.specifiers.map(({ imported }: any) => imported.name),
78
80
  );
@@ -70,14 +70,14 @@ const rule: Rule.RuleModule = {
70
70
  if (
71
71
  node.callee.type === 'Identifier' &&
72
72
  (!targetedFunctionsSwitch.has(node.callee.name) ||
73
- !isIdentifierImportedFrom(node.callee.name, IMPORT_SOURCES, context))
73
+ !isIdentifierImportedFrom(node.callee.name, IMPORT_SOURCES, context, node))
74
74
  ) {
75
75
  return;
76
76
  }
77
77
 
78
78
  const nameArgument = node.arguments[0];
79
79
  if (nameArgument.type === 'Identifier') {
80
- const def = getDef(nameArgument.name, context);
80
+ const def = getDef(nameArgument.name, context, node);
81
81
  if (def != null && def.type === 'Variable') {
82
82
  const { value } = def.node.init as any;
83
83
 
@@ -24,7 +24,7 @@ const rule: Rule.RuleModule = {
24
24
  ) => {
25
25
  if (
26
26
  node.object.type === 'Identifier' &&
27
- isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context)
27
+ isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context, node)
28
28
  ) {
29
29
  context.report({
30
30
  messageId: 'useRecommended',
@@ -42,7 +42,7 @@ const rule: Rule.RuleModule = {
42
42
  ) => {
43
43
  if (
44
44
  node.object.type === 'Identifier' &&
45
- isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context)
45
+ isIdentifierImportedFrom(node.object.name, BANNED_IMPORTS_SET, context, node)
46
46
  ) {
47
47
  context.report({
48
48
  messageId: 'notSupported',
@@ -1,15 +1,17 @@
1
1
  import { FEATURE_API_IMPORT_SOURCES } from '../constants';
2
2
  import type { Rule, Scope } from 'eslint';
3
+ import type { Node as EstreeNode } from 'estree';
4
+ import { getScope } from '../util/context-compat';
3
5
 
4
6
  export function isIdentifierImportedFrom(
5
7
  identifierName: string,
6
8
  sources: Set<string>,
7
9
  context: Rule.RuleContext,
10
+ node: EstreeNode,
8
11
  ) {
9
12
  if (sources.size > 0) {
10
13
  return (
11
- context
12
- .getScope()
14
+ getScope(context, node)
13
15
  .references.find((ref) => ref.identifier.name === identifierName)
14
16
  ?.resolved?.defs.some(
15
17
  (def) =>
@@ -21,13 +23,13 @@ export function isIdentifierImportedFrom(
21
23
  return false;
22
24
  }
23
25
 
24
- export function isAPIimport(functionName: string, context: Rule.RuleContext) {
25
- return isIdentifierImportedFrom(functionName, FEATURE_API_IMPORT_SOURCES, context);
26
+ export function isAPIimport(functionName: string, context: Rule.RuleContext, node: EstreeNode) {
27
+ return isIdentifierImportedFrom(functionName, FEATURE_API_IMPORT_SOURCES, context, node);
26
28
  }
27
29
 
28
30
  // returns the definition node of a variable if it's declared within the scope of the file
29
- export function getDef(name: string, context: Rule.RuleContext) {
30
- let scope: Scope.Scope | null = context.getScope();
31
+ export function getDef(name: string, context: Rule.RuleContext, node: EstreeNode) {
32
+ let scope: Scope.Scope | null = getScope(context, node);
31
33
 
32
34
  while (scope && scope.type !== 'global') {
33
35
  for (const variable of scope.variables) {
@@ -0,0 +1,28 @@
1
+ import type { Rule, Scope, SourceCode } from 'eslint';
2
+ import type { Node } from 'estree';
3
+
4
+ /**
5
+ * TODO: Consider whether this should be replaced by ESLint's compat library.
6
+ * Either way, this should be removed once we no longer need to support ESLint versions less than 8.40.
7
+ */
8
+
9
+ /**
10
+ * A compatibility layer to support older versions of ESLint.
11
+ * `context.sourceCode` is the preferred way to access SourceCode, as
12
+ * `context.getSourceCode()` is deprecated in v8 and removed in v9.
13
+ * @param context - The ESLint rule context
14
+ */
15
+ const getSourceCode = (context: Rule.RuleContext): SourceCode => {
16
+ return context.sourceCode ?? context.getSourceCode();
17
+ };
18
+
19
+ /**
20
+ * A compatibility layer to support older versions of ESLint.
21
+ * `context.sourceCode.getScope()` is the preferred way to access Scope, as
22
+ * `context.getScope()` was removed in v9.
23
+ * @param context - The ESLint rule context
24
+ * @param node - The node to get the scope for
25
+ */
26
+ export const getScope = (context: Rule.RuleContext, node: Node): Scope.Scope => {
27
+ return getSourceCode(context)?.getScope(node) ?? context.getScope();
28
+ };
package/tsconfig.app.json CHANGED
@@ -33,6 +33,9 @@
33
33
  "outDir": "../../../tsDist/@atlaskit__eslint-plugin-platform/app"
34
34
  },
35
35
  "references": [
36
+ {
37
+ "path": "../../design-system/eslint-utils/tsconfig.app.json"
38
+ },
36
39
  {
37
40
  "path": "../../monorepo-tooling/ts-loader/tsconfig.app.json"
38
41
  }