@atlaskit/eslint-plugin-platform 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +17 -9
- package/dist/cjs/rules/constants.js +1 -1
- package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +5 -5
- package/dist/cjs/rules/ensure-no-private-dependencies/index.js +48 -66
- package/dist/cjs/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/cjs/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/cjs/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/cjs/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/cjs/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/cjs/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/cjs/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/cjs/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/cjs/rules/feature-gating/valid-gate-name/index.js +60 -0
- package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +871 -0
- package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +1384 -0
- package/dist/cjs/rules/import/no-conversation-assistant-barrel-imports/index.js +43 -0
- package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +1401 -0
- package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +777 -0
- package/dist/cjs/rules/import/shared/barrel-parsing.js +511 -0
- package/dist/cjs/rules/import/shared/file-system.js +186 -0
- package/dist/cjs/rules/import/shared/jest-utils.js +191 -0
- package/dist/cjs/rules/import/shared/package-registry.js +263 -0
- package/dist/cjs/rules/import/shared/package-resolution.js +185 -0
- package/dist/cjs/rules/import/shared/perf.js +89 -0
- package/dist/cjs/rules/import/shared/types.js +67 -0
- package/dist/cjs/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/cjs/rules/no-sparse-checkout/index.js +1 -1
- package/dist/cjs/rules/prefer-crypto-random-uuid/index.js +87 -0
- package/dist/cjs/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/es2019/index.js +17 -9
- package/dist/es2019/rules/constants.js +1 -1
- package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +5 -5
- package/dist/es2019/rules/ensure-no-private-dependencies/index.js +10 -9
- package/dist/es2019/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/es2019/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/es2019/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/es2019/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/es2019/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/es2019/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/es2019/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/es2019/rules/feature-gating/valid-gate-name/index.js +52 -0
- package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +801 -0
- package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +1113 -0
- package/dist/es2019/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
- package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +1179 -0
- package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +738 -0
- package/dist/es2019/rules/import/shared/barrel-parsing.js +433 -0
- package/dist/es2019/rules/import/shared/file-system.js +174 -0
- package/dist/es2019/rules/import/shared/jest-utils.js +159 -0
- package/dist/es2019/rules/import/shared/package-registry.js +240 -0
- package/dist/es2019/rules/import/shared/package-resolution.js +161 -0
- package/dist/es2019/rules/import/shared/perf.js +83 -0
- package/dist/es2019/rules/import/shared/types.js +57 -0
- package/dist/es2019/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/es2019/rules/no-sparse-checkout/index.js +1 -1
- package/dist/es2019/rules/prefer-crypto-random-uuid/index.js +81 -0
- package/dist/es2019/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/esm/index.js +17 -9
- package/dist/esm/rules/constants.js +1 -1
- package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +5 -5
- package/dist/esm/rules/ensure-no-private-dependencies/index.js +48 -65
- package/dist/esm/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/esm/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/esm/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/esm/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/esm/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/esm/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/esm/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/esm/rules/feature-gating/valid-gate-name/index.js +53 -0
- package/dist/esm/rules/import/no-barrel-entry-imports/index.js +864 -0
- package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +1375 -0
- package/dist/esm/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
- package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +1391 -0
- package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +770 -0
- package/dist/esm/rules/import/shared/barrel-parsing.js +500 -0
- package/dist/esm/rules/import/shared/file-system.js +176 -0
- package/dist/esm/rules/import/shared/jest-utils.js +179 -0
- package/dist/esm/rules/import/shared/package-registry.js +256 -0
- package/dist/esm/rules/import/shared/package-resolution.js +175 -0
- package/dist/esm/rules/import/shared/perf.js +80 -0
- package/dist/esm/rules/import/shared/types.js +61 -0
- package/dist/esm/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/esm/rules/no-sparse-checkout/index.js +1 -1
- package/dist/esm/rules/prefer-crypto-random-uuid/index.js +81 -0
- package/dist/esm/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/types/index.d.ts +18 -16
- package/dist/types/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
- package/dist/types/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
- package/dist/types/rules/import/no-conversation-assistant-barrel-imports/index.d.ts +3 -0
- package/dist/types/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
- package/dist/types/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
- package/dist/types/rules/import/shared/barrel-parsing.d.ts +30 -0
- package/dist/types/rules/import/shared/file-system.d.ts +38 -0
- package/dist/types/rules/import/shared/jest-utils.d.ts +47 -0
- package/dist/types/rules/import/shared/package-registry.d.ts +26 -0
- package/dist/types/rules/import/shared/package-resolution.d.ts +38 -0
- package/dist/types/rules/import/shared/perf.d.ts +13 -0
- package/dist/types/rules/import/shared/types.d.ts +131 -0
- package/dist/types/rules/prefer-crypto-random-uuid/index.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +18 -28
- package/dist/types-ts4.5/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
- package/dist/types-ts4.5/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
- package/dist/types-ts4.5/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
- package/dist/types-ts4.5/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
- package/dist/types-ts4.5/rules/import/shared/barrel-parsing.d.ts +30 -0
- package/dist/types-ts4.5/rules/import/shared/file-system.d.ts +38 -0
- package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +47 -0
- package/dist/types-ts4.5/rules/import/shared/package-registry.d.ts +26 -0
- package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +38 -0
- package/dist/types-ts4.5/rules/import/shared/perf.d.ts +13 -0
- package/dist/types-ts4.5/rules/import/shared/types.d.ts +131 -0
- package/package.json +4 -5
- package/dist/cjs/rules/ensure-feature-flag-prefix/index.js +0 -75
- package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +0 -158
- package/dist/es2019/rules/ensure-feature-flag-prefix/index.js +0 -65
- package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +0 -146
- package/dist/esm/rules/ensure-feature-flag-prefix/index.js +0 -69
- package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +0 -151
- /package/dist/types/rules/{ensure-native-and-af-exports-synced → feature-gating/valid-gate-name}/index.d.ts +0 -0
- /package/dist/types-ts4.5/rules/{ensure-feature-flag-prefix → feature-gating/valid-gate-name}/index.d.ts +0 -0
- /package/dist/types-ts4.5/rules/{ensure-native-and-af-exports-synced → import/no-conversation-assistant-barrel-imports}/index.d.ts +0 -0
- /package/dist/{types/rules/ensure-feature-flag-prefix → types-ts4.5/rules/prefer-crypto-random-uuid}/index.d.ts +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PERF_ENV_VAR = void 0;
|
|
7
|
+
exports.isPerfEnabled = isPerfEnabled;
|
|
8
|
+
exports.perfInc = perfInc;
|
|
9
|
+
exports.perfTime = perfTime;
|
|
10
|
+
var PERF_ENV_VAR = exports.PERF_ENV_VAR = 'INTERNAL_ESLINT_BARREL_PERF';
|
|
11
|
+
function nowMs() {
|
|
12
|
+
// eslint-disable-next-line no-restricted-globals
|
|
13
|
+
return typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now();
|
|
14
|
+
}
|
|
15
|
+
function isPerfEnabled() {
|
|
16
|
+
return process.env[PERF_ENV_VAR] === '1' || process.env[PERF_ENV_VAR] === 'true';
|
|
17
|
+
}
|
|
18
|
+
function ensurePerfInitialized(_ref) {
|
|
19
|
+
var fs = _ref.fs;
|
|
20
|
+
if (!fs.cache.perf) {
|
|
21
|
+
fs.cache.perf = {
|
|
22
|
+
installedExitHook: false,
|
|
23
|
+
counters: {},
|
|
24
|
+
timers: {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function ensureExitHookInstalled(_ref2) {
|
|
29
|
+
var fs = _ref2.fs;
|
|
30
|
+
if (!isPerfEnabled()) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
ensurePerfInitialized({
|
|
34
|
+
fs: fs
|
|
35
|
+
});
|
|
36
|
+
if (fs.cache.perf.installedExitHook) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
fs.cache.perf.installedExitHook = true;
|
|
40
|
+
// eslint-disable-next-line no-console
|
|
41
|
+
console.error("[eslint-plugin-internal] perf enabled via ".concat(PERF_ENV_VAR));
|
|
42
|
+
process.once('exit', function () {
|
|
43
|
+
var _fs$cache$perf$counte, _fs$cache$perf, _fs$cache$perf$timers, _fs$cache$perf2;
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.error("[eslint-plugin-internal] perf exit hook fired (".concat(PERF_ENV_VAR, ")"));
|
|
46
|
+
// eslint-disable-next-line no-console
|
|
47
|
+
console.log("[eslint-plugin-internal] perf summary (".concat(PERF_ENV_VAR, ")"));
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.log(JSON.stringify({
|
|
50
|
+
counters: (_fs$cache$perf$counte = (_fs$cache$perf = fs.cache.perf) === null || _fs$cache$perf === void 0 ? void 0 : _fs$cache$perf.counters) !== null && _fs$cache$perf$counte !== void 0 ? _fs$cache$perf$counte : {},
|
|
51
|
+
timers: (_fs$cache$perf$timers = (_fs$cache$perf2 = fs.cache.perf) === null || _fs$cache$perf2 === void 0 ? void 0 : _fs$cache$perf2.timers) !== null && _fs$cache$perf$timers !== void 0 ? _fs$cache$perf$timers : {}
|
|
52
|
+
}, null, 2));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function perfInc(_ref3) {
|
|
56
|
+
var _perf$counters$key;
|
|
57
|
+
var fs = _ref3.fs,
|
|
58
|
+
key = _ref3.key,
|
|
59
|
+
_ref3$by = _ref3.by,
|
|
60
|
+
by = _ref3$by === void 0 ? 1 : _ref3$by;
|
|
61
|
+
if (!isPerfEnabled()) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
ensureExitHookInstalled({
|
|
65
|
+
fs: fs
|
|
66
|
+
});
|
|
67
|
+
var perf = fs.cache.perf;
|
|
68
|
+
perf.counters[key] = ((_perf$counters$key = perf.counters[key]) !== null && _perf$counters$key !== void 0 ? _perf$counters$key : 0) + by;
|
|
69
|
+
}
|
|
70
|
+
function perfTime(_ref4) {
|
|
71
|
+
var fs = _ref4.fs,
|
|
72
|
+
key = _ref4.key,
|
|
73
|
+
fn = _ref4.fn;
|
|
74
|
+
if (!isPerfEnabled()) {
|
|
75
|
+
return fn();
|
|
76
|
+
}
|
|
77
|
+
ensureExitHookInstalled({
|
|
78
|
+
fs: fs
|
|
79
|
+
});
|
|
80
|
+
var perf = fs.cache.perf;
|
|
81
|
+
var start = nowMs();
|
|
82
|
+
try {
|
|
83
|
+
return fn();
|
|
84
|
+
} finally {
|
|
85
|
+
var _perf$timers$key;
|
|
86
|
+
var duration = nowMs() - start;
|
|
87
|
+
perf.timers[key] = ((_perf$timers$key = perf.timers[key]) !== null && _perf$timers$key !== void 0 ? _perf$timers$key : 0) + duration;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.realFileSystem = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _child_process = require("child_process");
|
|
10
|
+
var _fs = require("fs");
|
|
11
|
+
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; }
|
|
12
|
+
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) { (0, _defineProperty2.default)(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; }
|
|
13
|
+
/**
|
|
14
|
+
* Directory entry returned by readdirSync with withFileTypes option.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* State for the package registry cache.
|
|
19
|
+
* This is used to cache package name to directory mappings for efficient lookups.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Cache structure for file system operations.
|
|
24
|
+
* Contains both package registry cache and workspace root cache.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* File system abstraction for testability.
|
|
29
|
+
* This interface allows the core logic to be tested with mock file systems.
|
|
30
|
+
* The cache property holds package resolution state and can be passed as an empty
|
|
31
|
+
* object for tests to ensure fresh state for each test case.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Real file system implementation using Node.js fs module.
|
|
36
|
+
*/
|
|
37
|
+
var realFileSystem = exports.realFileSystem = {
|
|
38
|
+
existsSync: _fs.existsSync,
|
|
39
|
+
readFileSync: _fs.readFileSync,
|
|
40
|
+
realpathSync: _fs.realpathSync,
|
|
41
|
+
statSync: _fs.statSync,
|
|
42
|
+
readdirSync: function readdirSync(path, options) {
|
|
43
|
+
return (0, _fs.readdirSync)(path, options);
|
|
44
|
+
},
|
|
45
|
+
execSync: function execSync(command, options) {
|
|
46
|
+
try {
|
|
47
|
+
return (0, _child_process.execSync)(command, _objectSpread(_objectSpread({}, options), {}, {
|
|
48
|
+
encoding: 'utf-8'
|
|
49
|
+
})).trim();
|
|
50
|
+
} catch (_unused) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
cache: {}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Information about cross-package re-export origin.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Information about where an export originates.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Context for package resolution operations.
|
|
67
|
+
*/
|
|
@@ -8,7 +8,7 @@ exports.default = void 0;
|
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
10
10
|
|
|
11
|
-
var STORYBOOK_DECORATOR_IDENTIFIER = '
|
|
11
|
+
var STORYBOOK_DECORATOR_IDENTIFIER = 'withPlatformFeatureGates';
|
|
12
12
|
var rule = {
|
|
13
13
|
meta: {
|
|
14
14
|
hasSuggestions: false,
|
|
@@ -7,7 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
// We will be removing sparse checkout from pipelines in CI completely due to the load it causes on BBC.
|
|
8
8
|
// We will be incrementally removing sparse-checkout from the files below as it is probably unnecessasry.
|
|
9
9
|
// If you must add an exception below, please go through the chopper process before doing so
|
|
10
|
-
var sparseCheckoutExceptions = ['
|
|
10
|
+
var sparseCheckoutExceptions = ['pipeline-definitions/pipelines/custom/run-issue-automat.ts', 'pipeline-definitions/pipelines/custom/marketplace/utils.ts', 'pipeline-definitions/pipelines/custom/confluence/utils/index.ts', 'pipeline-definitions/pipelines/custom/afm-tools/upload-afm-dependency-graph-cache.ts', 'pipeline-definitions/pipelines/custom/afm-tools/default-afm-tools.ts', 'pipeline-definitions/pipelines/custom/marketplace/utils.ts', 'pipeline-definitions/pipelines/custom/afm-git-hooks.ts', 'pipeline-definitions/pipelines/custom/update-codeowners-and-teams-gen.ts', 'pipeline-definitions/pipelines/custom/run-issue-automat.ts'];
|
|
11
11
|
var rule = {
|
|
12
12
|
meta: {
|
|
13
13
|
docs: {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
8
|
+
|
|
9
|
+
var rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'problem',
|
|
12
|
+
// Problem type = can be error
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Prefer crypto.randomUUID() over uuid library. The uuid package causes Jest mocking issues.',
|
|
15
|
+
recommended: true
|
|
16
|
+
},
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
// Enables --fix
|
|
19
|
+
messages: {
|
|
20
|
+
preferCryptoRandomUUID: 'Use crypto.randomUUID() instead of the uuid library. Run `eslint --fix` to auto-migrate.'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
create: function create(context) {
|
|
24
|
+
var uuidImports = new Map(); // Track imported names
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
28
|
+
if (node.type !== 'ImportDeclaration') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
var source = node.source.value;
|
|
32
|
+
if (typeof source === 'string' && (source === 'uuid' || /^uuid\/v[14]$/.test(source))) {
|
|
33
|
+
// Track imported name (e.g., uuid, v4, etc.)
|
|
34
|
+
var specifier = node.specifiers[0];
|
|
35
|
+
if (specifier) {
|
|
36
|
+
uuidImports.set(specifier.local.name, node);
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node: node,
|
|
40
|
+
messageId: 'preferCryptoRandomUUID',
|
|
41
|
+
fix: function fix(fixer) {
|
|
42
|
+
// Remove the import - usages will be fixed separately
|
|
43
|
+
return fixer.remove(node);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
CallExpression: function CallExpression(node) {
|
|
49
|
+
var _node$arguments$;
|
|
50
|
+
if (node.type !== 'CallExpression') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle direct uuid() or v4() calls
|
|
55
|
+
if (node.callee.type === 'Identifier') {
|
|
56
|
+
var calleeName = node.callee.name;
|
|
57
|
+
if (uuidImports.has(calleeName)) {
|
|
58
|
+
context.report({
|
|
59
|
+
node: node,
|
|
60
|
+
messageId: 'preferCryptoRandomUUID',
|
|
61
|
+
fix: function fix(fixer) {
|
|
62
|
+
// Replace uuid() with crypto.randomUUID()
|
|
63
|
+
return fixer.replaceText(node.callee, 'crypto.randomUUID');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Handle require('uuid')
|
|
70
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'require' && ((_node$arguments$ = node.arguments[0]) === null || _node$arguments$ === void 0 ? void 0 : _node$arguments$.type) === 'Literal') {
|
|
71
|
+
var arg = node.arguments[0].value;
|
|
72
|
+
if (typeof arg === 'string' && (arg === 'uuid' || /^uuid\/v[14]$/.test(arg))) {
|
|
73
|
+
context.report({
|
|
74
|
+
node: node,
|
|
75
|
+
messageId: 'preferCryptoRandomUUID'
|
|
76
|
+
// require() needs manual refactoring
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
'Program:exit': function ProgramExit() {
|
|
82
|
+
uuidImports.clear();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var _default = exports.default = rule;
|
|
@@ -7,7 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
var rule = {
|
|
8
8
|
meta: {
|
|
9
9
|
docs: {
|
|
10
|
-
url: 'https://
|
|
10
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/use-entrypoints-in-examples/README.md',
|
|
11
11
|
description: 'Encourage usage of package entrypoints in examples.'
|
|
12
12
|
},
|
|
13
13
|
messages: {
|
package/dist/es2019/index.js
CHANGED
|
@@ -7,14 +7,12 @@ import ensureTestRunnerNestedCount from './rules/ensure-test-runner-nested-count
|
|
|
7
7
|
import ensureAtlassianTeam from './rules/ensure-atlassian-team';
|
|
8
8
|
import noDuplicateDependencies from './rules/no-duplicate-dependencies';
|
|
9
9
|
import noInvalidFeatureFlagUsage from './rules/no-invalid-feature-flag-usage';
|
|
10
|
-
import ensureFeatureFlagPrefix from './rules/ensure-feature-flag-prefix';
|
|
11
10
|
import ensureCriticalDependencyResolutions from './rules/ensure-critical-dependency-resolutions';
|
|
12
11
|
import ensureValidBinValues from './rules/ensure-valid-bin-values';
|
|
13
12
|
import ensureNoPrivateDependencies from './rules/ensure-no-private-dependencies';
|
|
14
13
|
import expandBorderShorthand from './rules/compiled/expand-border-shorthand';
|
|
15
14
|
import noInvalidStorybookDecoratorUsage from './rules/no-invalid-storybook-decorator-usage';
|
|
16
15
|
import ensurePublishValid from './rules/ensure-publish-valid';
|
|
17
|
-
import ensureNativeAndAfExportsSynced from './rules/ensure-native-and-af-exports-synced';
|
|
18
16
|
import noModuleLevelEval from './rules/feature-gating/no-module-level-eval';
|
|
19
17
|
import noModuleLevelEvalNav4 from './rules/feature-gating/no-module-level-eval-nav4';
|
|
20
18
|
import staticFeatureFlags from './rules/feature-gating/static-feature-flags';
|
|
@@ -24,11 +22,18 @@ import preferFG from './rules/feature-gating/prefer-fg';
|
|
|
24
22
|
import noAlias from './rules/feature-gating/no-alias';
|
|
25
23
|
import useEntrypointsInExamples from './rules/use-entrypoints-in-examples';
|
|
26
24
|
import useRecommendedUtils from './rules/feature-gating/use-recommended-utils';
|
|
25
|
+
import validGateName from './rules/feature-gating/valid-gate-name';
|
|
27
26
|
import expandBackgroundShorthand from './rules/compiled/expand-background-shorthand';
|
|
28
27
|
import expandSpacingShorthand from './rules/compiled/expand-spacing-shorthand';
|
|
29
28
|
import noSparseCheckout from './rules/no-sparse-checkout';
|
|
30
29
|
import noDirectDocumentUsage from './rules/no-direct-document-usage';
|
|
31
30
|
import noSetImmediate from './rules/no-set-immediate';
|
|
31
|
+
import preferCryptoRandomUuid from './rules/prefer-crypto-random-uuid';
|
|
32
|
+
import noBarrelEntryImports from './rules/import/no-barrel-entry-imports';
|
|
33
|
+
import noBarrelEntryJestMock from './rules/import/no-barrel-entry-jest-mock';
|
|
34
|
+
import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
|
|
35
|
+
import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-imports';
|
|
36
|
+
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
32
37
|
import { join, normalize } from 'node:path';
|
|
33
38
|
import { readFileSync } from 'node:fs';
|
|
34
39
|
let jiraRoot;
|
|
@@ -50,7 +55,6 @@ try {
|
|
|
50
55
|
const packageJson = require('@atlaskit/eslint-plugin-platform/package.json');
|
|
51
56
|
const rules = {
|
|
52
57
|
'ensure-feature-flag-registration': ensureFeatureFlagRegistration,
|
|
53
|
-
'ensure-feature-flag-prefix': ensureFeatureFlagPrefix,
|
|
54
58
|
'ensure-test-runner-arguments': ensureTestRunnerArguments,
|
|
55
59
|
'ensure-test-runner-nested-count': ensureTestRunnerNestedCount,
|
|
56
60
|
'ensure-atlassian-team': ensureAtlassianTeam,
|
|
@@ -65,7 +69,6 @@ const rules = {
|
|
|
65
69
|
'no-pre-post-install-scripts': noPreAndPostInstallScripts,
|
|
66
70
|
'no-invalid-storybook-decorator-usage': noInvalidStorybookDecoratorUsage,
|
|
67
71
|
'ensure-publish-valid': ensurePublishValid,
|
|
68
|
-
'ensure-native-and-af-exports-synced': ensureNativeAndAfExportsSynced,
|
|
69
72
|
'no-module-level-eval': noModuleLevelEval,
|
|
70
73
|
'no-module-level-eval-nav4': noModuleLevelEvalNav4,
|
|
71
74
|
'static-feature-flags': staticFeatureFlags,
|
|
@@ -75,9 +78,16 @@ const rules = {
|
|
|
75
78
|
'no-alias': noAlias,
|
|
76
79
|
'use-entrypoints-in-examples': useEntrypointsInExamples,
|
|
77
80
|
'use-recommended-utils': useRecommendedUtils,
|
|
81
|
+
'valid-gate-name': validGateName,
|
|
78
82
|
'no-sparse-checkout': noSparseCheckout,
|
|
79
83
|
'no-direct-document-usage': noDirectDocumentUsage,
|
|
80
|
-
'no-set-immediate': noSetImmediate
|
|
84
|
+
'no-set-immediate': noSetImmediate,
|
|
85
|
+
'prefer-crypto-random-uuid': preferCryptoRandomUuid,
|
|
86
|
+
'no-barrel-entry-imports': noBarrelEntryImports,
|
|
87
|
+
'no-barrel-entry-jest-mock': noBarrelEntryJestMock,
|
|
88
|
+
'no-jest-mock-barrel-files': noJestMockBarrelFiles,
|
|
89
|
+
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
90
|
+
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports
|
|
81
91
|
};
|
|
82
92
|
const commonConfig = {
|
|
83
93
|
'@atlaskit/platform/ensure-test-runner-arguments': 'error',
|
|
@@ -108,11 +118,9 @@ const recommendedRules = {
|
|
|
108
118
|
'@atlaskit/platform/inline-usage': 'error',
|
|
109
119
|
'@atlaskit/platform/prefer-fg': 'error',
|
|
110
120
|
'@atlaskit/platform/no-alias': 'error',
|
|
121
|
+
'@atlaskit/platform/valid-gate-name': 'error',
|
|
111
122
|
// end: feature-gating rules
|
|
112
|
-
'@atlaskit/platform/ensure-feature-flag-registration': 'error'
|
|
113
|
-
'@atlaskit/platform/ensure-feature-flag-prefix': ['warn', {
|
|
114
|
-
allowedPrefixes: ['platform.', 'platform_']
|
|
115
|
-
}]
|
|
123
|
+
'@atlaskit/platform/ensure-feature-flag-registration': 'error'
|
|
116
124
|
};
|
|
117
125
|
const jiraRules = commonConfig;
|
|
118
126
|
const jsonPrefix = '/* eslint-disable quote-props, comma-dangle, quotes, semi, eol-last, @typescript-eslint/semi, no-template-curly-in-string */ module.exports = ';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// List of libraries that we maintain or have worked on
|
|
2
2
|
// - eg `@atlaskit/feature-gate-js-client` shouldn't be included in here
|
|
3
|
-
export const FEATURE_API_IMPORT_SOURCES = new Set(['@atlassian/jira-feature-flagging', '@atlassian/jira-feature-flagging-using-meta', '@atlassian/jira-feature-gating', '@atlassian/jira-feature-experiments', '@atlaskit/platform-feature-flags']);
|
|
3
|
+
export const FEATURE_API_IMPORT_SOURCES = new Set(['@atlassian/jira-feature-flagging', '@atlassian/jira-feature-flagging-using-meta', '@atlassian/jira-feature-gating', '@atlassian/jira-feature-experiments', '@atlaskit/platform-feature-flags', '@atlassian/repo-feature-flags-statsig']);
|
|
4
4
|
export const FEATURE_MOCKS_IMPORT_SOURCES = new Set(['@atlassian/jira-feature-flagging-mocks', '@atlassian/jira-feature-gates-test-mocks', '@atlassian/jira-feature-gates-storybook-mocks']);
|
|
5
5
|
export const FEATURE_UTILS_IMPORT_SOURCES = new Set(['@atlassian/jira-feature-flagging-utils', '@atlassian/jira-feature-gate-component']);
|
|
@@ -9,11 +9,11 @@ import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
|
9
9
|
const DESIRED_PKG_VERSIONS = {
|
|
10
10
|
typescript: ['5.4', '5.9'],
|
|
11
11
|
tslib: ['2.6', '2.8'],
|
|
12
|
-
'@types/react': ['16.14', '18.2'],
|
|
13
|
-
'react-relay': ['npm:atl-react-relay@0.0.0-main-
|
|
14
|
-
'relay-compiler': ['npm:atl-relay-compiler@0.0.0-main-
|
|
15
|
-
'relay-runtime': ['npm:atl-relay-runtime@0.0.0-main-
|
|
16
|
-
'relay-test-utils': ['npm:atl-relay-test-utils@0.0.0-main-
|
|
12
|
+
'@types/react': ['16.14', '18.2', '18.3'],
|
|
13
|
+
'react-relay': ['npm:atl-react-relay@0.0.0-main-39e79f66'],
|
|
14
|
+
'relay-compiler': ['npm:atl-relay-compiler@0.0.0-main-39e79f66'],
|
|
15
|
+
'relay-runtime': ['npm:atl-relay-runtime@0.0.0-main-39e79f66'],
|
|
16
|
+
'relay-test-utils': ['npm:atl-relay-test-utils@0.0.0-main-39e79f66']
|
|
17
17
|
};
|
|
18
18
|
const matchMinorVersion = (desiredVersion, versionInResolutions) => {
|
|
19
19
|
const firstChar = versionInResolutions[0];
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { getObjectPropertyAsObject, getObjectPropertyAsLiteral } from '../util/handle-ast-object';
|
|
4
4
|
import { getPackagesSync } from '@manypkg/get-packages';
|
|
5
5
|
import { findRootSync } from '@manypkg/find-root';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
let root;
|
|
7
|
+
let pkgs;
|
|
8
|
+
let pkgMap;
|
|
9
9
|
const rule = {
|
|
10
10
|
meta: {
|
|
11
11
|
type: 'problem',
|
|
@@ -19,13 +19,14 @@ const rule = {
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
create(context) {
|
|
22
|
+
if (!context.filename.endsWith('package.json')) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
root ??= findRootSync(process.cwd());
|
|
26
|
+
pkgs ??= getPackagesSync(root).packages;
|
|
27
|
+
pkgMap ??= new Map(pkgs.map(pkg => [pkg.packageJson.name, pkg]));
|
|
22
28
|
return {
|
|
23
|
-
ObjectExpression:
|
|
24
|
-
// Only run this rule on package.json files
|
|
25
|
-
if (!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression') {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
+
ObjectExpression: node => {
|
|
29
30
|
// Private dependencies can be used in private packages
|
|
30
31
|
const isPrivatePkg = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
31
32
|
if (isPrivatePkg === true) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAPIimport } from '../utils';
|
|
2
|
-
const FUNCTION_NAMES = new Set(['ff', 'fg', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp']);
|
|
3
|
-
const STATSIG_ONLY_FUNCTION_NAMES = new Set(['fg', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp']);
|
|
2
|
+
const FUNCTION_NAMES = new Set(['checkGate', 'ff', 'fg', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp']);
|
|
3
|
+
const STATSIG_ONLY_FUNCTION_NAMES = new Set(['checkGate', 'fg', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp']);
|
|
4
4
|
const findDefinitionDeclaration = node => node.type === 'VariableDeclaration' || node.type === 'FunctionDeclaration' ? node : findDefinitionDeclaration(node.parent);
|
|
5
5
|
const validateCallExpression = (node, context) => {
|
|
6
6
|
const targetedFunctionsSwitch = context.options[0] === 'ssOnly' ? STATSIG_ONLY_FUNCTION_NAMES : FUNCTION_NAMES;
|
|
@@ -29,6 +29,12 @@ const validateBinaryExpression = (node, context) => {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
+
const validateAwaitExpression = (node, context) => {
|
|
33
|
+
if (node.argument.type === 'CallExpression') {
|
|
34
|
+
return validateCallExpression(node.argument, context);
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
32
38
|
const validateReturnExpression = ({
|
|
33
39
|
body
|
|
34
40
|
}, context) => {
|
|
@@ -42,6 +48,8 @@ const validateReturnExpression = ({
|
|
|
42
48
|
} = statement;
|
|
43
49
|
if (argument && argument.type === 'CallExpression') {
|
|
44
50
|
validateCallExpression(argument, context);
|
|
51
|
+
} else if (argument && argument.type === 'AwaitExpression') {
|
|
52
|
+
validateAwaitExpression(argument, context);
|
|
45
53
|
} else if (argument && argument.type === 'BinaryExpression') {
|
|
46
54
|
validateBinaryExpression(argument, context);
|
|
47
55
|
}
|
|
@@ -52,6 +60,9 @@ const validateFunctionBody = (body, context) => {
|
|
|
52
60
|
case 'CallExpression':
|
|
53
61
|
validateCallExpression(body, context);
|
|
54
62
|
break;
|
|
63
|
+
case 'AwaitExpression':
|
|
64
|
+
validateAwaitExpression(body, context);
|
|
65
|
+
break;
|
|
55
66
|
case 'BinaryExpression':
|
|
56
67
|
validateBinaryExpression(body, context);
|
|
57
68
|
break;
|
|
@@ -66,7 +77,7 @@ const rule = {
|
|
|
66
77
|
type: 'problem',
|
|
67
78
|
docs: {
|
|
68
79
|
description: 'Ensure feature flags/gates and experiments are inlined so that they can be statically analyzable.',
|
|
69
|
-
url: 'https://
|
|
80
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/inline-usage/README.md'
|
|
70
81
|
},
|
|
71
82
|
schema: [{
|
|
72
83
|
enum: ['ssOnly']
|
|
@@ -4,7 +4,7 @@ const IMPORT_SOURCES = new Set([...FEATURE_API_IMPORT_SOURCES, ...FEATURE_MOCKS_
|
|
|
4
4
|
const rule = {
|
|
5
5
|
meta: {
|
|
6
6
|
docs: {
|
|
7
|
-
url: 'https://
|
|
7
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/no-alias/README.md',
|
|
8
8
|
description: 'Disallow aliasing of feature flag utils to ensure feature flag usage is statically analyzable'
|
|
9
9
|
},
|
|
10
10
|
messages: {
|
|
@@ -18,7 +18,7 @@ const rule = {
|
|
|
18
18
|
meta: {
|
|
19
19
|
docs: {
|
|
20
20
|
description: 'Disallow feature flag usage at module level',
|
|
21
|
-
url: 'https://
|
|
21
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/no-module-level-eval/README.md'
|
|
22
22
|
},
|
|
23
23
|
messages: {
|
|
24
24
|
noModuleLevelEval: 'Do not evaluate feature flags in the module level, it will always resolve to false when server side rendered or when flags are loaded async.'
|
|
@@ -29,7 +29,7 @@ const rule = {
|
|
|
29
29
|
meta: {
|
|
30
30
|
docs: {
|
|
31
31
|
description: 'Disallow getWillShowNav4 or isVisualRefreshEnabled usage at module level',
|
|
32
|
-
url: 'https://
|
|
32
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/no-module-level-eval-nav4/README.md'
|
|
33
33
|
},
|
|
34
34
|
messages: {
|
|
35
35
|
noModuleLevelEval: 'Do not evaluate getWillShowNav4 or isVisualRefreshEnabled at module level. This causes complications with SSR. If feature flagging components in `jira` use `componentWithCondition` from `@atlassian/jira-feature-flagging-utils`.'
|
|
@@ -8,6 +8,9 @@ const getGateType = (node, context) => {
|
|
|
8
8
|
if (type === 'BinaryExpression') {
|
|
9
9
|
return getGateType(node.left, context) || getGateType(node.right, context);
|
|
10
10
|
}
|
|
11
|
+
if (type === 'AwaitExpression') {
|
|
12
|
+
return getGateType(node.argument, context);
|
|
13
|
+
}
|
|
11
14
|
if (node.type === 'CallExpression') {
|
|
12
15
|
const {
|
|
13
16
|
callee
|
|
@@ -47,7 +50,7 @@ const rule = {
|
|
|
47
50
|
meta: {
|
|
48
51
|
docs: {
|
|
49
52
|
description: 'Inform on how to use gates and experiments in logical expressions',
|
|
50
|
-
url: 'https://
|
|
53
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/no-preconditioning/README.md'
|
|
51
54
|
},
|
|
52
55
|
messages: {
|
|
53
56
|
useConfig: 'Do not precondition gates or experiments with another gate. Configure this in Statsig instead to reduce unnecessary code, simplify cleanup and to ensure accurate exposures in Statsig.',
|
|
@@ -33,7 +33,7 @@ const validateUsage = (node, utilName, context, changeMap) => {
|
|
|
33
33
|
const rule = {
|
|
34
34
|
meta: {
|
|
35
35
|
docs: {
|
|
36
|
-
url: 'https://
|
|
36
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/prefer-fg/README.md',
|
|
37
37
|
description: 'Keep usages of boolean feature flags consistent'
|
|
38
38
|
},
|
|
39
39
|
fixable: 'code',
|
|
@@ -3,13 +3,13 @@ import { getDef, isIdentifierImportedFrom } from '../utils';
|
|
|
3
3
|
const IMPORT_SOURCES = new Set([...FEATURE_API_IMPORT_SOURCES, '@atlassian/jira-feature-flagging-utils', '@atlassian/jira-feature-gate-component', '@atlassian/jira-feature-gates-test-mocks', '@atlassian/jira-feature-gates-storybook-mocks']);
|
|
4
4
|
|
|
5
5
|
// Any functions not in this list should be skipped for performance.
|
|
6
|
-
const FUNCTION_NAMES = new Set(['ff', 'fg', 'getFeatureFlagValue', '
|
|
6
|
+
const FUNCTION_NAMES = new Set(['ff', 'fg', 'getFeatureFlagValue', 'componentWithFF', 'componentWithFG', 'passGate', 'withGate', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp', 'mockExp', 'withExp', 'wasExperimentManuallyExposed']);
|
|
7
7
|
const STATSIG_ONLY_FUNCTION_NAMES = new Set(['fg', 'componentWithFG', 'passGate', 'withGate', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp', 'mockExp', 'withExp', 'wasExperimentManuallyExposed']);
|
|
8
8
|
const rule = {
|
|
9
9
|
meta: {
|
|
10
10
|
type: 'problem',
|
|
11
11
|
docs: {
|
|
12
|
-
url: 'https://
|
|
12
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/static-feature-flags/README.md',
|
|
13
13
|
description: 'Ensure feature flags or gates are static string literals'
|
|
14
14
|
},
|
|
15
15
|
fixable: 'code',
|
|
@@ -3,7 +3,7 @@ const BANNED_IMPORTS_SET = new Set(['@atlaskit/feature-gate-js-client']);
|
|
|
3
3
|
const rule = {
|
|
4
4
|
meta: {
|
|
5
5
|
docs: {
|
|
6
|
-
url: 'https://
|
|
6
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/use-recommended-utils/README.md',
|
|
7
7
|
description: 'Prefer using the feature flag abstraction over direct statsig library.'
|
|
8
8
|
},
|
|
9
9
|
messages: {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FEATURE_API_IMPORT_SOURCES } from '../../constants';
|
|
2
|
+
import { isIdentifierImportedFrom } from '../utils';
|
|
3
|
+
const IMPORT_SOURCES = new Set([...FEATURE_API_IMPORT_SOURCES, '@atlassian/jira-feature-flagging-utils', '@atlassian/jira-feature-gate-component']);
|
|
4
|
+
const FUNCTION_NAMES = new Set(['ff', 'fg', 'getFeatureFlagValue', 'componentWithFF', 'componentWithFG', 'passGate', 'withGate', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp', 'mockExp', 'withExp', 'wasExperimentManuallyExposed']);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Valid gate names must only contain lowercase letters (a-z), numbers (0-9),
|
|
8
|
+
* underscores (_), hyphens (-), and dots (.).
|
|
9
|
+
* No spaces, capital letters, or other characters are allowed.
|
|
10
|
+
*/
|
|
11
|
+
const VALID_GATE_NAME_PATTERN = /^[a-z0-9_.-]+$/;
|
|
12
|
+
function isValidGateName(name) {
|
|
13
|
+
return VALID_GATE_NAME_PATTERN.test(name);
|
|
14
|
+
}
|
|
15
|
+
const rule = {
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'problem',
|
|
18
|
+
docs: {
|
|
19
|
+
description: 'Ensure feature gate names contain only lowercase letters, numbers, underscores, and hyphens'
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
invalidGateName: 'Feature gate name "{{name}}" is invalid. Gate names must contain only lowercase letters (a-z), numbers (0-9), underscores (_), hyphens (-), and dots (.).'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
create(context) {
|
|
26
|
+
return {
|
|
27
|
+
'CallExpression[callee.type="Identifier"][arguments.length>0][arguments.0.type="Literal"]': node => {
|
|
28
|
+
if (node.type !== 'CallExpression') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (node.callee.type === 'Identifier' && (!FUNCTION_NAMES.has(node.callee.name) || !isIdentifierImportedFrom(node.callee.name, IMPORT_SOURCES, context, node))) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const nameArgument = node.arguments[0];
|
|
35
|
+
if (nameArgument.type !== 'Literal' || typeof nameArgument.value !== 'string') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const gateName = nameArgument.value;
|
|
39
|
+
if (!isValidGateName(gateName)) {
|
|
40
|
+
context.report({
|
|
41
|
+
node: nameArgument,
|
|
42
|
+
messageId: 'invalidGateName',
|
|
43
|
+
data: {
|
|
44
|
+
name: gateName
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
export default rule;
|