@atlaskit/eslint-plugin-platform 0.0.3
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 +13 -0
- package/README.md +6 -0
- package/build/tsconfig.json +17 -0
- package/dist/cjs/index.js +21 -0
- package/dist/cjs/rules/ensure-feature-flag-registration/index.js +102 -0
- package/dist/cjs/version.json +5 -0
- package/dist/es2019/index.js +12 -0
- package/dist/es2019/rules/ensure-feature-flag-registration/index.js +94 -0
- package/dist/es2019/version.json +5 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/rules/ensure-feature-flag-registration/index.js +94 -0
- package/dist/esm/version.json +5 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/rules/ensure-feature-flag-registration/index.d.ts +3 -0
- package/index.js +24 -0
- package/package.json +34 -0
- package/report.api.md +48 -0
- package/src/__tests__/utils/_tester.tsx +25 -0
- package/src/index.tsx +14 -0
- package/src/rules/ensure-feature-flag-registration/__tests__/unit/rule.test.tsx +87 -0
- package/src/rules/ensure-feature-flag-registration/index.tsx +115 -0
- package/tmp/api-report-tmp.d.ts +26 -0
- package/tsconfig.json +24 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @atlaskit/eslint-plugin-platform
|
|
2
|
+
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`11706c3e7c5`](https://bitbucket.org/atlassian/atlassian-frontend/commits/11706c3e7c5) - Publish platform eslint rules to npm to be consumed in other products
|
|
8
|
+
|
|
9
|
+
## 0.0.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`85dc0230439`](https://bitbucket.org/atlassian/atlassian-frontend/commits/85dc0230439) - Add eslint rule to allow for platform feature flag usage
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "es5",
|
|
5
|
+
"paths": {}
|
|
6
|
+
},
|
|
7
|
+
"include": [
|
|
8
|
+
"../src/**/*.ts",
|
|
9
|
+
"../src/**/*.tsx"
|
|
10
|
+
],
|
|
11
|
+
"exclude": [
|
|
12
|
+
"../src/**/__tests__/*",
|
|
13
|
+
"../__tests__/*",
|
|
14
|
+
"../src/**/*.test.*",
|
|
15
|
+
"../src/**/test.*"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.rules = exports.configs = void 0;
|
|
8
|
+
var _ensureFeatureFlagRegistration = _interopRequireDefault(require("./rules/ensure-feature-flag-registration"));
|
|
9
|
+
var rules = {
|
|
10
|
+
'ensure-feature-flag-registration': _ensureFeatureFlagRegistration.default
|
|
11
|
+
};
|
|
12
|
+
exports.rules = rules;
|
|
13
|
+
var configs = {
|
|
14
|
+
recommended: {
|
|
15
|
+
plugins: ['@atlaskit/platform'],
|
|
16
|
+
rules: {
|
|
17
|
+
'@atlaskit/platform/ensure-feature-flag-registration': 'error'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
exports.configs = configs;
|
|
@@ -0,0 +1,102 @@
|
|
|
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 _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _readPkgUp = _interopRequireDefault(require("read-pkg-up"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
// make sure we cache reading the package.json so we don't end up reading it for every instance of this rule.
|
|
12
|
+
var pkgJsonCache = new Map();
|
|
13
|
+
|
|
14
|
+
// get the ancestor package.json for a given file
|
|
15
|
+
var getPackageJsonForFileName = function getPackageJsonForFileName(filename) {
|
|
16
|
+
var splitFilename = filename.split(_path.default.sep);
|
|
17
|
+
for (var i = 0; i < splitFilename.length; i++) {
|
|
18
|
+
// attempt to search using the filename in the cache to see if we've read the package.json for a sibling file before
|
|
19
|
+
var searchPath = _path.default.join.apply(_path.default, (0, _toConsumableArray2.default)(splitFilename.splice(0, i)));
|
|
20
|
+
var cachedPkgJson = pkgJsonCache.get(searchPath);
|
|
21
|
+
if (cachedPkgJson) {
|
|
22
|
+
return cachedPkgJson;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
var _ref = _readPkgUp.default.sync({
|
|
26
|
+
cwd: filename,
|
|
27
|
+
normalize: false
|
|
28
|
+
}),
|
|
29
|
+
packageJson = _ref.packageJson,
|
|
30
|
+
pkgJsonPath = _ref.path;
|
|
31
|
+
pkgJsonCache.set(pkgJsonPath, packageJson);
|
|
32
|
+
return packageJson;
|
|
33
|
+
};
|
|
34
|
+
var rule = {
|
|
35
|
+
meta: {
|
|
36
|
+
hasSuggestions: false,
|
|
37
|
+
docs: {
|
|
38
|
+
recommended: false
|
|
39
|
+
},
|
|
40
|
+
type: 'problem',
|
|
41
|
+
messages: {
|
|
42
|
+
onlyInlineIf: "Only call feature flags as part of an expression, don't assign to a variable! See http://go/pff-eslint for more details",
|
|
43
|
+
onlyStringLiteral: "Only get feature flags by string literal, don't use variables! See http://go/pff-eslint for more details",
|
|
44
|
+
registrationSectionMissing: 'Please add a "platform-feature-flags" section to your package.json! See http://go/pff-eslint for more details',
|
|
45
|
+
featureFlagMissing: "Please add a \"{{ featureFlag }}\" section to the \"platform-feature-flags\" section in your package.json. See http://go/pff-eslint for more details"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
create: function create(context) {
|
|
49
|
+
return {
|
|
50
|
+
'CallExpression[callee.name=/getBooleanFF/]': function CallExpressionCalleeNameGetBooleanFF(node) {
|
|
51
|
+
// to make typescript happy
|
|
52
|
+
if (node.type === 'CallExpression') {
|
|
53
|
+
var _node$parent;
|
|
54
|
+
var args = node.arguments;
|
|
55
|
+
if (args.length === 1 && args[0].type !== 'Literal') {
|
|
56
|
+
return context.report({
|
|
57
|
+
node: node,
|
|
58
|
+
messageId: 'onlyStringLiteral'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
switch ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) {
|
|
62
|
+
case 'IfStatement':
|
|
63
|
+
case 'ConditionalExpression':
|
|
64
|
+
case 'LogicalExpression':
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
return context.report({
|
|
68
|
+
node: node,
|
|
69
|
+
messageId: 'onlyInlineIf'
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
var filename = context.getFilename();
|
|
74
|
+
var packageJson = getPackageJsonForFileName(filename);
|
|
75
|
+
var platformFeatureFlags = packageJson['platform-feature-flags'];
|
|
76
|
+
if (!platformFeatureFlags) {
|
|
77
|
+
return context.report({
|
|
78
|
+
node: node,
|
|
79
|
+
messageId: 'registrationSectionMissing'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (args.length === 1 && args[0].type === 'Literal' && args[0].raw) {
|
|
83
|
+
var featureFlag = args[0].value;
|
|
84
|
+
var featureFlagRegistration = platformFeatureFlags[featureFlag];
|
|
85
|
+
if (!featureFlagRegistration) {
|
|
86
|
+
return context.report({
|
|
87
|
+
node: node,
|
|
88
|
+
messageId: 'featureFlagMissing',
|
|
89
|
+
data: {
|
|
90
|
+
featureFlag: featureFlag
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var _default = rule;
|
|
102
|
+
exports.default = _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ensureFeatureFlagRegistration from './rules/ensure-feature-flag-registration';
|
|
2
|
+
export const rules = {
|
|
3
|
+
'ensure-feature-flag-registration': ensureFeatureFlagRegistration
|
|
4
|
+
};
|
|
5
|
+
export const configs = {
|
|
6
|
+
recommended: {
|
|
7
|
+
plugins: ['@atlaskit/platform'],
|
|
8
|
+
rules: {
|
|
9
|
+
'@atlaskit/platform/ensure-feature-flag-registration': 'error'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import readPkgUp from 'read-pkg-up';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
// make sure we cache reading the package.json so we don't end up reading it for every instance of this rule.
|
|
4
|
+
const pkgJsonCache = new Map();
|
|
5
|
+
|
|
6
|
+
// get the ancestor package.json for a given file
|
|
7
|
+
const getPackageJsonForFileName = filename => {
|
|
8
|
+
const splitFilename = filename.split(path.sep);
|
|
9
|
+
for (let i = 0; i < splitFilename.length; i++) {
|
|
10
|
+
// attempt to search using the filename in the cache to see if we've read the package.json for a sibling file before
|
|
11
|
+
const searchPath = path.join(...splitFilename.splice(0, i));
|
|
12
|
+
const cachedPkgJson = pkgJsonCache.get(searchPath);
|
|
13
|
+
if (cachedPkgJson) {
|
|
14
|
+
return cachedPkgJson;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const {
|
|
18
|
+
packageJson,
|
|
19
|
+
path: pkgJsonPath
|
|
20
|
+
} = readPkgUp.sync({
|
|
21
|
+
cwd: filename,
|
|
22
|
+
normalize: false
|
|
23
|
+
});
|
|
24
|
+
pkgJsonCache.set(pkgJsonPath, packageJson);
|
|
25
|
+
return packageJson;
|
|
26
|
+
};
|
|
27
|
+
const rule = {
|
|
28
|
+
meta: {
|
|
29
|
+
hasSuggestions: false,
|
|
30
|
+
docs: {
|
|
31
|
+
recommended: false
|
|
32
|
+
},
|
|
33
|
+
type: 'problem',
|
|
34
|
+
messages: {
|
|
35
|
+
onlyInlineIf: "Only call feature flags as part of an expression, don't assign to a variable! See http://go/pff-eslint for more details",
|
|
36
|
+
onlyStringLiteral: "Only get feature flags by string literal, don't use variables! See http://go/pff-eslint for more details",
|
|
37
|
+
registrationSectionMissing: 'Please add a "platform-feature-flags" section to your package.json! See http://go/pff-eslint for more details',
|
|
38
|
+
featureFlagMissing: `Please add a "{{ featureFlag }}" section to the "platform-feature-flags" section in your package.json. See http://go/pff-eslint for more details`
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
create(context) {
|
|
42
|
+
return {
|
|
43
|
+
'CallExpression[callee.name=/getBooleanFF/]': node => {
|
|
44
|
+
// to make typescript happy
|
|
45
|
+
if (node.type === 'CallExpression') {
|
|
46
|
+
var _node$parent;
|
|
47
|
+
const args = node.arguments;
|
|
48
|
+
if (args.length === 1 && args[0].type !== 'Literal') {
|
|
49
|
+
return context.report({
|
|
50
|
+
node,
|
|
51
|
+
messageId: 'onlyStringLiteral'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
switch ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) {
|
|
55
|
+
case 'IfStatement':
|
|
56
|
+
case 'ConditionalExpression':
|
|
57
|
+
case 'LogicalExpression':
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
return context.report({
|
|
61
|
+
node,
|
|
62
|
+
messageId: 'onlyInlineIf'
|
|
63
|
+
});
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
const filename = context.getFilename();
|
|
67
|
+
const packageJson = getPackageJsonForFileName(filename);
|
|
68
|
+
const platformFeatureFlags = packageJson['platform-feature-flags'];
|
|
69
|
+
if (!platformFeatureFlags) {
|
|
70
|
+
return context.report({
|
|
71
|
+
node,
|
|
72
|
+
messageId: 'registrationSectionMissing'
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (args.length === 1 && args[0].type === 'Literal' && args[0].raw) {
|
|
76
|
+
const featureFlag = args[0].value;
|
|
77
|
+
const featureFlagRegistration = platformFeatureFlags[featureFlag];
|
|
78
|
+
if (!featureFlagRegistration) {
|
|
79
|
+
return context.report({
|
|
80
|
+
node,
|
|
81
|
+
messageId: 'featureFlagMissing',
|
|
82
|
+
data: {
|
|
83
|
+
featureFlag
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
export default rule;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ensureFeatureFlagRegistration from './rules/ensure-feature-flag-registration';
|
|
2
|
+
export var rules = {
|
|
3
|
+
'ensure-feature-flag-registration': ensureFeatureFlagRegistration
|
|
4
|
+
};
|
|
5
|
+
export var configs = {
|
|
6
|
+
recommended: {
|
|
7
|
+
plugins: ['@atlaskit/platform'],
|
|
8
|
+
rules: {
|
|
9
|
+
'@atlaskit/platform/ensure-feature-flag-registration': 'error'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import readPkgUp from 'read-pkg-up';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
// make sure we cache reading the package.json so we don't end up reading it for every instance of this rule.
|
|
5
|
+
var pkgJsonCache = new Map();
|
|
6
|
+
|
|
7
|
+
// get the ancestor package.json for a given file
|
|
8
|
+
var getPackageJsonForFileName = function getPackageJsonForFileName(filename) {
|
|
9
|
+
var splitFilename = filename.split(path.sep);
|
|
10
|
+
for (var i = 0; i < splitFilename.length; i++) {
|
|
11
|
+
// attempt to search using the filename in the cache to see if we've read the package.json for a sibling file before
|
|
12
|
+
var searchPath = path.join.apply(path, _toConsumableArray(splitFilename.splice(0, i)));
|
|
13
|
+
var cachedPkgJson = pkgJsonCache.get(searchPath);
|
|
14
|
+
if (cachedPkgJson) {
|
|
15
|
+
return cachedPkgJson;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var _ref = readPkgUp.sync({
|
|
19
|
+
cwd: filename,
|
|
20
|
+
normalize: false
|
|
21
|
+
}),
|
|
22
|
+
packageJson = _ref.packageJson,
|
|
23
|
+
pkgJsonPath = _ref.path;
|
|
24
|
+
pkgJsonCache.set(pkgJsonPath, packageJson);
|
|
25
|
+
return packageJson;
|
|
26
|
+
};
|
|
27
|
+
var rule = {
|
|
28
|
+
meta: {
|
|
29
|
+
hasSuggestions: false,
|
|
30
|
+
docs: {
|
|
31
|
+
recommended: false
|
|
32
|
+
},
|
|
33
|
+
type: 'problem',
|
|
34
|
+
messages: {
|
|
35
|
+
onlyInlineIf: "Only call feature flags as part of an expression, don't assign to a variable! See http://go/pff-eslint for more details",
|
|
36
|
+
onlyStringLiteral: "Only get feature flags by string literal, don't use variables! See http://go/pff-eslint for more details",
|
|
37
|
+
registrationSectionMissing: 'Please add a "platform-feature-flags" section to your package.json! See http://go/pff-eslint for more details',
|
|
38
|
+
featureFlagMissing: "Please add a \"{{ featureFlag }}\" section to the \"platform-feature-flags\" section in your package.json. See http://go/pff-eslint for more details"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
create: function create(context) {
|
|
42
|
+
return {
|
|
43
|
+
'CallExpression[callee.name=/getBooleanFF/]': function CallExpressionCalleeNameGetBooleanFF(node) {
|
|
44
|
+
// to make typescript happy
|
|
45
|
+
if (node.type === 'CallExpression') {
|
|
46
|
+
var _node$parent;
|
|
47
|
+
var args = node.arguments;
|
|
48
|
+
if (args.length === 1 && args[0].type !== 'Literal') {
|
|
49
|
+
return context.report({
|
|
50
|
+
node: node,
|
|
51
|
+
messageId: 'onlyStringLiteral'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
switch ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) {
|
|
55
|
+
case 'IfStatement':
|
|
56
|
+
case 'ConditionalExpression':
|
|
57
|
+
case 'LogicalExpression':
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
return context.report({
|
|
61
|
+
node: node,
|
|
62
|
+
messageId: 'onlyInlineIf'
|
|
63
|
+
});
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
var filename = context.getFilename();
|
|
67
|
+
var packageJson = getPackageJsonForFileName(filename);
|
|
68
|
+
var platformFeatureFlags = packageJson['platform-feature-flags'];
|
|
69
|
+
if (!platformFeatureFlags) {
|
|
70
|
+
return context.report({
|
|
71
|
+
node: node,
|
|
72
|
+
messageId: 'registrationSectionMissing'
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (args.length === 1 && args[0].type === 'Literal' && args[0].raw) {
|
|
76
|
+
var featureFlag = args[0].value;
|
|
77
|
+
var featureFlagRegistration = platformFeatureFlags[featureFlag];
|
|
78
|
+
if (!featureFlagRegistration) {
|
|
79
|
+
return context.report({
|
|
80
|
+
node: node,
|
|
81
|
+
messageId: 'featureFlagMissing',
|
|
82
|
+
data: {
|
|
83
|
+
featureFlag: featureFlag
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
export default rule;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const rules: {
|
|
2
|
+
'ensure-feature-flag-registration': import("eslint").Rule.RuleModule;
|
|
3
|
+
};
|
|
4
|
+
export declare const configs: {
|
|
5
|
+
recommended: {
|
|
6
|
+
plugins: string[];
|
|
7
|
+
rules: {
|
|
8
|
+
'@atlaskit/platform/ensure-feature-flag-registration': string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
2
|
+
/* eslint-disable global-require */
|
|
3
|
+
// Used only for internal repo usage.
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const paths = require('tsconfig-paths');
|
|
7
|
+
|
|
8
|
+
if (!require.extensions['.ts']) {
|
|
9
|
+
// ts-node can only handle being registered once, see https://github.com/TypeStrong/ts-node/issues/409
|
|
10
|
+
require('ts-node').register({
|
|
11
|
+
project: path.join(__dirname, 'tsconfig.json'),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// We programatically register tsconfig paths here so it picks up the tsconfig here
|
|
17
|
+
// instead of in root CWD.
|
|
18
|
+
paths.register(paths.loadConfig(__dirname));
|
|
19
|
+
} catch (e) {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.log(e);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = require('./src/index');
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/eslint-plugin-platform",
|
|
3
|
+
"description": "The essential plugin for use with Atlassian frontend platform tools",
|
|
4
|
+
"version": "0.0.3",
|
|
5
|
+
"author": "Atlassian Pty Ltd",
|
|
6
|
+
"atlassian": {
|
|
7
|
+
"team": "UIP - Platform Integration Trust (PITa)",
|
|
8
|
+
"releaseModel": "continuous",
|
|
9
|
+
"inPublicMirror": true
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"registry": "https://registry.npmjs.org/"
|
|
13
|
+
},
|
|
14
|
+
"repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
|
|
15
|
+
"main": "dist/cjs/index.js",
|
|
16
|
+
"module": "dist/esm/index.js",
|
|
17
|
+
"module:es2019": "dist/es2019/index.js",
|
|
18
|
+
"types": "dist/types/index.d.ts",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"atlaskit:src": "src/index.tsx",
|
|
21
|
+
"af:exports": {
|
|
22
|
+
".": "./src/index.tsx"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@babel/runtime": "^7.0.0",
|
|
26
|
+
"read-pkg-up": "^7.0.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
|
|
30
|
+
"eslint": "^7.7.0",
|
|
31
|
+
"tsconfig-paths": "^3.9.0"
|
|
32
|
+
},
|
|
33
|
+
"prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.1"
|
|
34
|
+
}
|
package/report.api.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- API Report Version: 2.3 -->
|
|
2
|
+
|
|
3
|
+
## API Report File for "@atlaskit/eslint-plugin-platform"
|
|
4
|
+
|
|
5
|
+
> Do not edit this file. This report is auto-generated using [API Extractor](https://api-extractor.com/).
|
|
6
|
+
> [Learn more about API reports](https://hello.atlassian.net/wiki/spaces/UR/pages/1825484529/Package+API+Reports)
|
|
7
|
+
|
|
8
|
+
### Table of contents
|
|
9
|
+
|
|
10
|
+
- [Main Entry Types](#main-entry-types)
|
|
11
|
+
- [Peer Dependencies](#peer-dependencies)
|
|
12
|
+
|
|
13
|
+
### Main Entry Types
|
|
14
|
+
|
|
15
|
+
<!--SECTION START: Main Entry Types-->
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Rule } from 'eslint';
|
|
19
|
+
|
|
20
|
+
// @public (undocumented)
|
|
21
|
+
export const configs: {
|
|
22
|
+
recommended: {
|
|
23
|
+
plugins: string[];
|
|
24
|
+
rules: {
|
|
25
|
+
'@atlaskit/platform/ensure-feature-flag-registration': string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// @public (undocumented)
|
|
31
|
+
export const rules: {
|
|
32
|
+
'ensure-feature-flag-registration': Rule.RuleModule;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// (No @packageDocumentation comment for this package)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
<!--SECTION END: Main Entry Types-->
|
|
39
|
+
|
|
40
|
+
### Peer Dependencies
|
|
41
|
+
|
|
42
|
+
<!--SECTION START: Peer Dependencies-->
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
<!--SECTION END: Peer Dependencies-->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/fs/filename-pattern-match */
|
|
2
|
+
/* eslint-disable no-undef */
|
|
3
|
+
import { RuleTester } from 'eslint';
|
|
4
|
+
|
|
5
|
+
(RuleTester as any).describe = (text: string, method: Function) => {
|
|
6
|
+
const origHasAssertions = expect.hasAssertions;
|
|
7
|
+
describe(text, () => {
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
// Stub out expect.hasAssertions beforeEach from jest-presetup.js
|
|
10
|
+
expect.hasAssertions = () => {};
|
|
11
|
+
});
|
|
12
|
+
afterAll(() => {
|
|
13
|
+
expect.hasAssertions = origHasAssertions;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
method();
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
export const tester = new RuleTester({
|
|
20
|
+
parser: require.resolve('babel-eslint'),
|
|
21
|
+
parserOptions: {
|
|
22
|
+
ecmaVersion: 6,
|
|
23
|
+
sourceType: 'module',
|
|
24
|
+
},
|
|
25
|
+
});
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import ensureFeatureFlagRegistration from './rules/ensure-feature-flag-registration';
|
|
2
|
+
|
|
3
|
+
export const rules = {
|
|
4
|
+
'ensure-feature-flag-registration': ensureFeatureFlagRegistration,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const configs = {
|
|
8
|
+
recommended: {
|
|
9
|
+
plugins: ['@atlaskit/platform'],
|
|
10
|
+
rules: {
|
|
11
|
+
'@atlaskit/platform/ensure-feature-flag-registration': 'error',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
+
import rule from '../../index';
|
|
3
|
+
import { PackageJson } from 'read-pkg-up';
|
|
4
|
+
|
|
5
|
+
let mockPath = 'test/package.json';
|
|
6
|
+
|
|
7
|
+
let mockPackageJson: PackageJson = {
|
|
8
|
+
'platform-feature-flags': {
|
|
9
|
+
'test-flag': {
|
|
10
|
+
type: 'boolean',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
jest.mock('read-pkg-up', () => ({
|
|
15
|
+
sync: () => ({
|
|
16
|
+
path: mockPath,
|
|
17
|
+
packageJson: mockPackageJson,
|
|
18
|
+
}),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
describe('with existing platform-feature-flags section', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockPath = 'test/package.json';
|
|
24
|
+
|
|
25
|
+
mockPackageJson = {
|
|
26
|
+
'platform-feature-flags': {
|
|
27
|
+
'test-flag': {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
tester.run('ensure-feature-flag-registration', rule, {
|
|
35
|
+
valid: [
|
|
36
|
+
{
|
|
37
|
+
// IfStatement
|
|
38
|
+
code: `if(getBooleanFF('test-flag')) { }`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
// ConditionalExpression
|
|
42
|
+
code: `const val = getBooleanFF('test-flag') ? 'yay' : 'no';`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
// LogicalExpression
|
|
46
|
+
code: `const val = 100 + (getBooleanFF('test-flag') && 50 || 10);`,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
invalid: [
|
|
50
|
+
{
|
|
51
|
+
code: `getBooleanFF('test-flag')`,
|
|
52
|
+
errors: [{ messageId: 'onlyInlineIf' }],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
code: `const val = getBooleanFF('test-flag')`,
|
|
56
|
+
errors: [{ messageId: 'onlyInlineIf' }],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
code: `if(getBooleanFF('invalid-flag')) { }`,
|
|
60
|
+
errors: [{ messageId: 'featureFlagMissing' }],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: `const ff = "test-flag"; if(getBooleanFF(ff)) { }`,
|
|
64
|
+
errors: [{ messageId: 'onlyStringLiteral' }],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('with missing platform-feature-flags section', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
// change path to bust cache
|
|
73
|
+
mockPath = 'invalid-pkg/package.json';
|
|
74
|
+
mockPackageJson = {};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
tester.run('ensure-feature-flag-registration', rule, {
|
|
78
|
+
valid: [],
|
|
79
|
+
invalid: [
|
|
80
|
+
{
|
|
81
|
+
filename: 'other-directory/index.ts',
|
|
82
|
+
code: `if(getBooleanFF('test-flag')) { }`,
|
|
83
|
+
errors: [{ messageId: 'registrationSectionMissing' }],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import readPkgUp from 'read-pkg-up';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
type PlatformFeatureFlagRegistrationSection = {
|
|
6
|
+
[key: string]: {
|
|
7
|
+
type: 'boolean';
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// make sure we cache reading the package.json so we don't end up reading it for every instance of this rule.
|
|
12
|
+
const pkgJsonCache = new Map<string, readPkgUp.PackageJson>();
|
|
13
|
+
|
|
14
|
+
// get the ancestor package.json for a given file
|
|
15
|
+
const getPackageJsonForFileName = (filename: string): readPkgUp.PackageJson => {
|
|
16
|
+
const splitFilename = filename.split(path.sep);
|
|
17
|
+
for (let i = 0; i < splitFilename.length; i++) {
|
|
18
|
+
// attempt to search using the filename in the cache to see if we've read the package.json for a sibling file before
|
|
19
|
+
const searchPath = path.join(...splitFilename.splice(0, i));
|
|
20
|
+
const cachedPkgJson = pkgJsonCache.get(searchPath);
|
|
21
|
+
|
|
22
|
+
if (cachedPkgJson) {
|
|
23
|
+
return cachedPkgJson;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { packageJson, path: pkgJsonPath } = readPkgUp.sync({
|
|
28
|
+
cwd: filename,
|
|
29
|
+
normalize: false,
|
|
30
|
+
})!;
|
|
31
|
+
|
|
32
|
+
pkgJsonCache.set(pkgJsonPath, packageJson);
|
|
33
|
+
return packageJson;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const rule: Rule.RuleModule = {
|
|
37
|
+
meta: {
|
|
38
|
+
hasSuggestions: false,
|
|
39
|
+
docs: {
|
|
40
|
+
recommended: false,
|
|
41
|
+
},
|
|
42
|
+
type: 'problem',
|
|
43
|
+
messages: {
|
|
44
|
+
onlyInlineIf:
|
|
45
|
+
"Only call feature flags as part of an expression, don't assign to a variable! See http://go/pff-eslint for more details",
|
|
46
|
+
onlyStringLiteral:
|
|
47
|
+
"Only get feature flags by string literal, don't use variables! See http://go/pff-eslint for more details",
|
|
48
|
+
registrationSectionMissing:
|
|
49
|
+
'Please add a "platform-feature-flags" section to your package.json! See http://go/pff-eslint for more details',
|
|
50
|
+
featureFlagMissing: `Please add a "{{ featureFlag }}" section to the "platform-feature-flags" section in your package.json. See http://go/pff-eslint for more details`,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
create(context) {
|
|
54
|
+
return {
|
|
55
|
+
'CallExpression[callee.name=/getBooleanFF/]': (node: Rule.Node) => {
|
|
56
|
+
// to make typescript happy
|
|
57
|
+
if (node.type === 'CallExpression') {
|
|
58
|
+
const args = node.arguments;
|
|
59
|
+
|
|
60
|
+
if (args.length === 1 && args[0].type !== 'Literal') {
|
|
61
|
+
return context.report({
|
|
62
|
+
node,
|
|
63
|
+
messageId: 'onlyStringLiteral',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (node.parent?.type) {
|
|
68
|
+
case 'IfStatement':
|
|
69
|
+
case 'ConditionalExpression':
|
|
70
|
+
case 'LogicalExpression':
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
return context.report({
|
|
74
|
+
node,
|
|
75
|
+
messageId: 'onlyInlineIf',
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const filename = context.getFilename();
|
|
81
|
+
const packageJson = getPackageJsonForFileName(filename);
|
|
82
|
+
const platformFeatureFlags = packageJson[
|
|
83
|
+
'platform-feature-flags'
|
|
84
|
+
] as PlatformFeatureFlagRegistrationSection;
|
|
85
|
+
|
|
86
|
+
if (!platformFeatureFlags) {
|
|
87
|
+
return context.report({
|
|
88
|
+
node,
|
|
89
|
+
messageId: 'registrationSectionMissing',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (args.length === 1 && args[0].type === 'Literal' && args[0].raw) {
|
|
94
|
+
const featureFlag = args[0].value as string;
|
|
95
|
+
const featureFlagRegistration = platformFeatureFlags[featureFlag];
|
|
96
|
+
|
|
97
|
+
if (!featureFlagRegistration) {
|
|
98
|
+
return context.report({
|
|
99
|
+
node,
|
|
100
|
+
messageId: 'featureFlagMissing',
|
|
101
|
+
data: {
|
|
102
|
+
featureFlag,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default rule;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## API Report File for "@atlaskit/eslint-plugin-platform"
|
|
2
|
+
|
|
3
|
+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
|
|
7
|
+
import { Rule } from 'eslint';
|
|
8
|
+
|
|
9
|
+
// @public (undocumented)
|
|
10
|
+
export const configs: {
|
|
11
|
+
recommended: {
|
|
12
|
+
plugins: string[];
|
|
13
|
+
rules: {
|
|
14
|
+
'@atlaskit/platform/ensure-feature-flag-registration': string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// @public (undocumented)
|
|
20
|
+
export const rules: {
|
|
21
|
+
'ensure-feature-flag-registration': Rule.RuleModule;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// (No @packageDocumentation comment for this package)
|
|
25
|
+
|
|
26
|
+
```
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.node.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"__tests__/**/*.ts",
|
|
5
|
+
"__tests__/**/*.tsx",
|
|
6
|
+
"docs/**/*.ts",
|
|
7
|
+
"docs/**/*.tsx",
|
|
8
|
+
"examples/**/*.ts",
|
|
9
|
+
"examples/**/*.tsx",
|
|
10
|
+
"src/**/*.ts",
|
|
11
|
+
"src/**/*.tsx",
|
|
12
|
+
"scripts"
|
|
13
|
+
],
|
|
14
|
+
"compilerOptions": {
|
|
15
|
+
"baseUrl": "./",
|
|
16
|
+
"lib": ["ES2021.String"]
|
|
17
|
+
},
|
|
18
|
+
"ts-node": {
|
|
19
|
+
// It is faster to skip typechecking.
|
|
20
|
+
"transpileOnly": true,
|
|
21
|
+
// We must load files for third party defs - https://typestrong.org/ts-node/docs/types
|
|
22
|
+
"files": true,
|
|
23
|
+
}
|
|
24
|
+
}
|