@atlaskit/eslint-plugin-platform 2.9.3 → 2.10.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 +7 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/rules/import/one-value-export-per-file/index.js +203 -0
- package/dist/es2019/index.js +2 -0
- package/dist/es2019/rules/import/one-value-export-per-file/index.js +191 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/rules/import/one-value-export-per-file/index.js +196 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/rules/import/one-value-export-per-file/index.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +2 -0
- package/dist/types-ts4.5/rules/import/one-value-export-per-file/index.d.ts +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-platform
|
|
2
2
|
|
|
3
|
+
## 2.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`ed426d1a0955c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ed426d1a0955c) -
|
|
8
|
+
Add `one-value-export-per-file` ESLint rule for reporting files with multiple local value exports.
|
|
9
|
+
|
|
3
10
|
## 2.9.3
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/dist/cjs/index.js
CHANGED
|
@@ -45,6 +45,7 @@ var _noBarrelEntryJestMock = _interopRequireDefault(require("./rules/import/no-b
|
|
|
45
45
|
var _noJestMockBarrelFiles = _interopRequireDefault(require("./rules/import/no-jest-mock-barrel-files"));
|
|
46
46
|
var _noRelativeBarrelFileImports = _interopRequireDefault(require("./rules/import/no-relative-barrel-file-imports"));
|
|
47
47
|
var _noConversationAssistantBarrelImports = _interopRequireDefault(require("./rules/import/no-conversation-assistant-barrel-imports"));
|
|
48
|
+
var _oneValueExportPerFile = _interopRequireDefault(require("./rules/import/one-value-export-per-file"));
|
|
48
49
|
var _visitExampleTypeImportRequired = _interopRequireDefault(require("./rules/visit-example-type-import-required"));
|
|
49
50
|
var _editorExampleTypeImportRequired = _interopRequireDefault(require("./rules/editor-example-type-import-required"));
|
|
50
51
|
var _ensureUseSyncExternalStoreServerSnapshot = _interopRequireDefault(require("./rules/ensure-use-sync-external-store-server-snapshot"));
|
|
@@ -107,6 +108,7 @@ var rules = exports.rules = {
|
|
|
107
108
|
'no-jest-mock-barrel-files': _noJestMockBarrelFiles.default,
|
|
108
109
|
'no-relative-barrel-file-imports': _noRelativeBarrelFileImports.default,
|
|
109
110
|
'no-conversation-assistant-barrel-imports': _noConversationAssistantBarrelImports.default,
|
|
111
|
+
'one-value-export-per-file': _oneValueExportPerFile.default,
|
|
110
112
|
'visit-example-type-import-required': _visitExampleTypeImportRequired.default,
|
|
111
113
|
'no-xcss-in-cx': _noXcssInCx.default,
|
|
112
114
|
'editor-example-type-import-required': _editorExampleTypeImportRequired.default,
|
|
@@ -0,0 +1,203 @@
|
|
|
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
|
+
function getPropertyName(node) {
|
|
10
|
+
if (!node) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
if (node.type === 'Identifier') {
|
|
14
|
+
return node.name;
|
|
15
|
+
}
|
|
16
|
+
if (node.type === 'Literal') {
|
|
17
|
+
return String(node.value);
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function isPrimitiveLiteral(declarator) {
|
|
22
|
+
function isPrimitiveExpression(node) {
|
|
23
|
+
if (!node) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
switch (node.type) {
|
|
27
|
+
case 'Literal':
|
|
28
|
+
return typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean' || node.value === null;
|
|
29
|
+
case 'TemplateLiteral':
|
|
30
|
+
return node.expressions.length === 0;
|
|
31
|
+
case 'Identifier':
|
|
32
|
+
return node.name === 'undefined';
|
|
33
|
+
case 'UnaryExpression':
|
|
34
|
+
return ['+', '-', '~', '!'].includes(node.operator) && isPrimitiveExpression(node.argument);
|
|
35
|
+
case 'BinaryExpression':
|
|
36
|
+
return isPrimitiveExpression(node.left) && isPrimitiveExpression(node.right);
|
|
37
|
+
case 'TSAsExpression':
|
|
38
|
+
case 'TSTypeAssertion':
|
|
39
|
+
case 'TSNonNullExpression':
|
|
40
|
+
return isPrimitiveExpression(node.expression);
|
|
41
|
+
default:
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return isPrimitiveExpression(declarator.init);
|
|
46
|
+
}
|
|
47
|
+
function collectBindingExports(node) {
|
|
48
|
+
if (!node) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
switch (node.type) {
|
|
52
|
+
case 'Identifier':
|
|
53
|
+
return [{
|
|
54
|
+
name: node.name,
|
|
55
|
+
loc: node.loc
|
|
56
|
+
}];
|
|
57
|
+
case 'ObjectPattern':
|
|
58
|
+
return node.properties.flatMap(function (property) {
|
|
59
|
+
if (property.type === 'RestElement') {
|
|
60
|
+
return collectBindingExports(property.argument);
|
|
61
|
+
}
|
|
62
|
+
return collectBindingExports(property.value);
|
|
63
|
+
});
|
|
64
|
+
case 'ArrayPattern':
|
|
65
|
+
return node.elements.flatMap(function (element) {
|
|
66
|
+
return collectBindingExports(element);
|
|
67
|
+
});
|
|
68
|
+
case 'AssignmentPattern':
|
|
69
|
+
return collectBindingExports(node.left);
|
|
70
|
+
case 'RestElement':
|
|
71
|
+
return collectBindingExports(node.argument);
|
|
72
|
+
default:
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function getDefaultExportName(node) {
|
|
77
|
+
var declaration = node.declaration;
|
|
78
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
79
|
+
return declaration.id.name;
|
|
80
|
+
}
|
|
81
|
+
return 'default';
|
|
82
|
+
}
|
|
83
|
+
function getDefaultExportLoc(node) {
|
|
84
|
+
var declaration = node.declaration;
|
|
85
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
86
|
+
return declaration.id.loc;
|
|
87
|
+
}
|
|
88
|
+
return node.loc;
|
|
89
|
+
}
|
|
90
|
+
function collectDeclarationExports(declaration, allowPrimitiveExports) {
|
|
91
|
+
if (!declaration) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
switch (declaration.type) {
|
|
95
|
+
case 'VariableDeclaration':
|
|
96
|
+
return declaration.declarations.flatMap(function (declarator) {
|
|
97
|
+
if (allowPrimitiveExports && isPrimitiveLiteral(declarator)) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
return collectBindingExports(declarator.id);
|
|
101
|
+
});
|
|
102
|
+
case 'FunctionDeclaration':
|
|
103
|
+
case 'ClassDeclaration':
|
|
104
|
+
return declaration.id ? [{
|
|
105
|
+
name: declaration.id.name,
|
|
106
|
+
loc: declaration.id.loc
|
|
107
|
+
}] : [];
|
|
108
|
+
case 'TSEnumDeclaration':
|
|
109
|
+
return [{
|
|
110
|
+
name: declaration.id.name,
|
|
111
|
+
loc: declaration.id.loc
|
|
112
|
+
}];
|
|
113
|
+
case 'TSInterfaceDeclaration':
|
|
114
|
+
case 'TSTypeAliasDeclaration':
|
|
115
|
+
return [];
|
|
116
|
+
default:
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function collectNamedSpecifierExports(node) {
|
|
121
|
+
if (node.exportKind === 'type') {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
return node.specifiers.flatMap(function (specifier) {
|
|
125
|
+
if (specifier.type !== 'ExportSpecifier' || specifier.exportKind === 'type') {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
var exportedName = getPropertyName(specifier.exported);
|
|
129
|
+
return exportedName ? [{
|
|
130
|
+
name: exportedName,
|
|
131
|
+
loc: specifier.exported.loc
|
|
132
|
+
}] : [];
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
var rule = {
|
|
136
|
+
meta: {
|
|
137
|
+
type: 'suggestion',
|
|
138
|
+
docs: {
|
|
139
|
+
description: 'Disallow more than one local value export per file.',
|
|
140
|
+
category: 'Best Practices',
|
|
141
|
+
recommended: false
|
|
142
|
+
},
|
|
143
|
+
schema: [{
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
allowPrimitiveExports: {
|
|
147
|
+
type: 'boolean',
|
|
148
|
+
description: 'When true, primitive value exports (strings, numbers, booleans, null, undefined, and template literals) are ignored when counting local value exports.'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
additionalProperties: false
|
|
152
|
+
}],
|
|
153
|
+
messages: {
|
|
154
|
+
multipleValueExports: 'This file exports {{count}} local values ({{names}}). Keep one value export per file https://hello.atlassian.net/wiki/spaces/DevInfra/pages/6809881812/One+Export+Per+File'
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
create: function create(context) {
|
|
158
|
+
var _, _options$allowPrimiti;
|
|
159
|
+
var options = (_ = context.options[0]) !== null && _ !== void 0 ? _ : {};
|
|
160
|
+
var allowPrimitiveExports = (_options$allowPrimiti = options.allowPrimitiveExports) !== null && _options$allowPrimiti !== void 0 ? _options$allowPrimiti : false;
|
|
161
|
+
var valueExports = [];
|
|
162
|
+
return {
|
|
163
|
+
ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
|
|
164
|
+
var exportNode = node;
|
|
165
|
+
valueExports.push({
|
|
166
|
+
name: getDefaultExportName(exportNode),
|
|
167
|
+
loc: getDefaultExportLoc(exportNode)
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
ExportNamedDeclaration: function ExportNamedDeclaration(node) {
|
|
171
|
+
var exportNode = node;
|
|
172
|
+
|
|
173
|
+
// Re-export-only barrel syntax is intentionally ignored.
|
|
174
|
+
if (exportNode.source) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
valueExports.push.apply(valueExports, (0, _toConsumableArray2.default)(collectDeclarationExports(exportNode.declaration, allowPrimitiveExports)));
|
|
178
|
+
valueExports.push.apply(valueExports, (0, _toConsumableArray2.default)(collectNamedSpecifierExports(exportNode)));
|
|
179
|
+
},
|
|
180
|
+
'Program:exit': function ProgramExit(node) {
|
|
181
|
+
if (valueExports.length <= 1) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
var sampleNames = valueExports.slice(0, 5).map(function (valueExport) {
|
|
185
|
+
return valueExport.name;
|
|
186
|
+
}).join(', ');
|
|
187
|
+
var names = valueExports.length > 5 ? "".concat(sampleNames, ", and ").concat(valueExports.length - 5, " more") : sampleNames;
|
|
188
|
+
valueExports.forEach(function (valueExport) {
|
|
189
|
+
context.report({
|
|
190
|
+
node: node,
|
|
191
|
+
loc: valueExport.loc,
|
|
192
|
+
messageId: 'multipleValueExports',
|
|
193
|
+
data: {
|
|
194
|
+
count: String(valueExports.length),
|
|
195
|
+
names: names
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var _default = exports.default = rule;
|
package/dist/es2019/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import noBarrelEntryJestMock from './rules/import/no-barrel-entry-jest-mock';
|
|
|
38
38
|
import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
|
|
39
39
|
import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-imports';
|
|
40
40
|
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
41
|
+
import oneValueExportPerFile from './rules/import/one-value-export-per-file';
|
|
41
42
|
import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
|
|
42
43
|
import editorExampleTypeImportRequired from './rules/editor-example-type-import-required';
|
|
43
44
|
import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
|
|
@@ -98,6 +99,7 @@ const rules = {
|
|
|
98
99
|
'no-jest-mock-barrel-files': noJestMockBarrelFiles,
|
|
99
100
|
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
100
101
|
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
|
|
102
|
+
'one-value-export-per-file': oneValueExportPerFile,
|
|
101
103
|
'visit-example-type-import-required': visitExampleTypeImportRequired,
|
|
102
104
|
'no-xcss-in-cx': noXcssInCx,
|
|
103
105
|
'editor-example-type-import-required': editorExampleTypeImportRequired,
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
function getPropertyName(node) {
|
|
2
|
+
if (!node) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
if (node.type === 'Identifier') {
|
|
6
|
+
return node.name;
|
|
7
|
+
}
|
|
8
|
+
if (node.type === 'Literal') {
|
|
9
|
+
return String(node.value);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function isPrimitiveLiteral(declarator) {
|
|
14
|
+
function isPrimitiveExpression(node) {
|
|
15
|
+
if (!node) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
switch (node.type) {
|
|
19
|
+
case 'Literal':
|
|
20
|
+
return typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean' || node.value === null;
|
|
21
|
+
case 'TemplateLiteral':
|
|
22
|
+
return node.expressions.length === 0;
|
|
23
|
+
case 'Identifier':
|
|
24
|
+
return node.name === 'undefined';
|
|
25
|
+
case 'UnaryExpression':
|
|
26
|
+
return ['+', '-', '~', '!'].includes(node.operator) && isPrimitiveExpression(node.argument);
|
|
27
|
+
case 'BinaryExpression':
|
|
28
|
+
return isPrimitiveExpression(node.left) && isPrimitiveExpression(node.right);
|
|
29
|
+
case 'TSAsExpression':
|
|
30
|
+
case 'TSTypeAssertion':
|
|
31
|
+
case 'TSNonNullExpression':
|
|
32
|
+
return isPrimitiveExpression(node.expression);
|
|
33
|
+
default:
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return isPrimitiveExpression(declarator.init);
|
|
38
|
+
}
|
|
39
|
+
function collectBindingExports(node) {
|
|
40
|
+
if (!node) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
switch (node.type) {
|
|
44
|
+
case 'Identifier':
|
|
45
|
+
return [{
|
|
46
|
+
name: node.name,
|
|
47
|
+
loc: node.loc
|
|
48
|
+
}];
|
|
49
|
+
case 'ObjectPattern':
|
|
50
|
+
return node.properties.flatMap(property => {
|
|
51
|
+
if (property.type === 'RestElement') {
|
|
52
|
+
return collectBindingExports(property.argument);
|
|
53
|
+
}
|
|
54
|
+
return collectBindingExports(property.value);
|
|
55
|
+
});
|
|
56
|
+
case 'ArrayPattern':
|
|
57
|
+
return node.elements.flatMap(element => collectBindingExports(element));
|
|
58
|
+
case 'AssignmentPattern':
|
|
59
|
+
return collectBindingExports(node.left);
|
|
60
|
+
case 'RestElement':
|
|
61
|
+
return collectBindingExports(node.argument);
|
|
62
|
+
default:
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function getDefaultExportName(node) {
|
|
67
|
+
const declaration = node.declaration;
|
|
68
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
69
|
+
return declaration.id.name;
|
|
70
|
+
}
|
|
71
|
+
return 'default';
|
|
72
|
+
}
|
|
73
|
+
function getDefaultExportLoc(node) {
|
|
74
|
+
const declaration = node.declaration;
|
|
75
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
76
|
+
return declaration.id.loc;
|
|
77
|
+
}
|
|
78
|
+
return node.loc;
|
|
79
|
+
}
|
|
80
|
+
function collectDeclarationExports(declaration, allowPrimitiveExports) {
|
|
81
|
+
if (!declaration) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
switch (declaration.type) {
|
|
85
|
+
case 'VariableDeclaration':
|
|
86
|
+
return declaration.declarations.flatMap(declarator => {
|
|
87
|
+
if (allowPrimitiveExports && isPrimitiveLiteral(declarator)) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
return collectBindingExports(declarator.id);
|
|
91
|
+
});
|
|
92
|
+
case 'FunctionDeclaration':
|
|
93
|
+
case 'ClassDeclaration':
|
|
94
|
+
return declaration.id ? [{
|
|
95
|
+
name: declaration.id.name,
|
|
96
|
+
loc: declaration.id.loc
|
|
97
|
+
}] : [];
|
|
98
|
+
case 'TSEnumDeclaration':
|
|
99
|
+
return [{
|
|
100
|
+
name: declaration.id.name,
|
|
101
|
+
loc: declaration.id.loc
|
|
102
|
+
}];
|
|
103
|
+
case 'TSInterfaceDeclaration':
|
|
104
|
+
case 'TSTypeAliasDeclaration':
|
|
105
|
+
return [];
|
|
106
|
+
default:
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function collectNamedSpecifierExports(node) {
|
|
111
|
+
if (node.exportKind === 'type') {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return node.specifiers.flatMap(specifier => {
|
|
115
|
+
if (specifier.type !== 'ExportSpecifier' || specifier.exportKind === 'type') {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const exportedName = getPropertyName(specifier.exported);
|
|
119
|
+
return exportedName ? [{
|
|
120
|
+
name: exportedName,
|
|
121
|
+
loc: specifier.exported.loc
|
|
122
|
+
}] : [];
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const rule = {
|
|
126
|
+
meta: {
|
|
127
|
+
type: 'suggestion',
|
|
128
|
+
docs: {
|
|
129
|
+
description: 'Disallow more than one local value export per file.',
|
|
130
|
+
category: 'Best Practices',
|
|
131
|
+
recommended: false
|
|
132
|
+
},
|
|
133
|
+
schema: [{
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
allowPrimitiveExports: {
|
|
137
|
+
type: 'boolean',
|
|
138
|
+
description: 'When true, primitive value exports (strings, numbers, booleans, null, undefined, and template literals) are ignored when counting local value exports.'
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
additionalProperties: false
|
|
142
|
+
}],
|
|
143
|
+
messages: {
|
|
144
|
+
multipleValueExports: 'This file exports {{count}} local values ({{names}}). Keep one value export per file https://hello.atlassian.net/wiki/spaces/DevInfra/pages/6809881812/One+Export+Per+File'
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
create(context) {
|
|
148
|
+
var _, _options$allowPrimiti;
|
|
149
|
+
const options = (_ = context.options[0]) !== null && _ !== void 0 ? _ : {};
|
|
150
|
+
const allowPrimitiveExports = (_options$allowPrimiti = options.allowPrimitiveExports) !== null && _options$allowPrimiti !== void 0 ? _options$allowPrimiti : false;
|
|
151
|
+
const valueExports = [];
|
|
152
|
+
return {
|
|
153
|
+
ExportDefaultDeclaration(node) {
|
|
154
|
+
const exportNode = node;
|
|
155
|
+
valueExports.push({
|
|
156
|
+
name: getDefaultExportName(exportNode),
|
|
157
|
+
loc: getDefaultExportLoc(exportNode)
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
ExportNamedDeclaration(node) {
|
|
161
|
+
const exportNode = node;
|
|
162
|
+
|
|
163
|
+
// Re-export-only barrel syntax is intentionally ignored.
|
|
164
|
+
if (exportNode.source) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
valueExports.push(...collectDeclarationExports(exportNode.declaration, allowPrimitiveExports));
|
|
168
|
+
valueExports.push(...collectNamedSpecifierExports(exportNode));
|
|
169
|
+
},
|
|
170
|
+
'Program:exit'(node) {
|
|
171
|
+
if (valueExports.length <= 1) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const sampleNames = valueExports.slice(0, 5).map(valueExport => valueExport.name).join(', ');
|
|
175
|
+
const names = valueExports.length > 5 ? `${sampleNames}, and ${valueExports.length - 5} more` : sampleNames;
|
|
176
|
+
valueExports.forEach(valueExport => {
|
|
177
|
+
context.report({
|
|
178
|
+
node,
|
|
179
|
+
loc: valueExport.loc,
|
|
180
|
+
messageId: 'multipleValueExports',
|
|
181
|
+
data: {
|
|
182
|
+
count: String(valueExports.length),
|
|
183
|
+
names
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
export default rule;
|
package/dist/esm/index.js
CHANGED
|
@@ -41,6 +41,7 @@ import noBarrelEntryJestMock from './rules/import/no-barrel-entry-jest-mock';
|
|
|
41
41
|
import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
|
|
42
42
|
import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-imports';
|
|
43
43
|
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
44
|
+
import oneValueExportPerFile from './rules/import/one-value-export-per-file';
|
|
44
45
|
import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
|
|
45
46
|
import editorExampleTypeImportRequired from './rules/editor-example-type-import-required';
|
|
46
47
|
import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
|
|
@@ -101,6 +102,7 @@ var rules = {
|
|
|
101
102
|
'no-jest-mock-barrel-files': noJestMockBarrelFiles,
|
|
102
103
|
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
103
104
|
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
|
|
105
|
+
'one-value-export-per-file': oneValueExportPerFile,
|
|
104
106
|
'visit-example-type-import-required': visitExampleTypeImportRequired,
|
|
105
107
|
'no-xcss-in-cx': noXcssInCx,
|
|
106
108
|
'editor-example-type-import-required': editorExampleTypeImportRequired,
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
function getPropertyName(node) {
|
|
3
|
+
if (!node) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
if (node.type === 'Identifier') {
|
|
7
|
+
return node.name;
|
|
8
|
+
}
|
|
9
|
+
if (node.type === 'Literal') {
|
|
10
|
+
return String(node.value);
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function isPrimitiveLiteral(declarator) {
|
|
15
|
+
function isPrimitiveExpression(node) {
|
|
16
|
+
if (!node) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
switch (node.type) {
|
|
20
|
+
case 'Literal':
|
|
21
|
+
return typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean' || node.value === null;
|
|
22
|
+
case 'TemplateLiteral':
|
|
23
|
+
return node.expressions.length === 0;
|
|
24
|
+
case 'Identifier':
|
|
25
|
+
return node.name === 'undefined';
|
|
26
|
+
case 'UnaryExpression':
|
|
27
|
+
return ['+', '-', '~', '!'].includes(node.operator) && isPrimitiveExpression(node.argument);
|
|
28
|
+
case 'BinaryExpression':
|
|
29
|
+
return isPrimitiveExpression(node.left) && isPrimitiveExpression(node.right);
|
|
30
|
+
case 'TSAsExpression':
|
|
31
|
+
case 'TSTypeAssertion':
|
|
32
|
+
case 'TSNonNullExpression':
|
|
33
|
+
return isPrimitiveExpression(node.expression);
|
|
34
|
+
default:
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return isPrimitiveExpression(declarator.init);
|
|
39
|
+
}
|
|
40
|
+
function collectBindingExports(node) {
|
|
41
|
+
if (!node) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
switch (node.type) {
|
|
45
|
+
case 'Identifier':
|
|
46
|
+
return [{
|
|
47
|
+
name: node.name,
|
|
48
|
+
loc: node.loc
|
|
49
|
+
}];
|
|
50
|
+
case 'ObjectPattern':
|
|
51
|
+
return node.properties.flatMap(function (property) {
|
|
52
|
+
if (property.type === 'RestElement') {
|
|
53
|
+
return collectBindingExports(property.argument);
|
|
54
|
+
}
|
|
55
|
+
return collectBindingExports(property.value);
|
|
56
|
+
});
|
|
57
|
+
case 'ArrayPattern':
|
|
58
|
+
return node.elements.flatMap(function (element) {
|
|
59
|
+
return collectBindingExports(element);
|
|
60
|
+
});
|
|
61
|
+
case 'AssignmentPattern':
|
|
62
|
+
return collectBindingExports(node.left);
|
|
63
|
+
case 'RestElement':
|
|
64
|
+
return collectBindingExports(node.argument);
|
|
65
|
+
default:
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function getDefaultExportName(node) {
|
|
70
|
+
var declaration = node.declaration;
|
|
71
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
72
|
+
return declaration.id.name;
|
|
73
|
+
}
|
|
74
|
+
return 'default';
|
|
75
|
+
}
|
|
76
|
+
function getDefaultExportLoc(node) {
|
|
77
|
+
var declaration = node.declaration;
|
|
78
|
+
if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') && declaration.id) {
|
|
79
|
+
return declaration.id.loc;
|
|
80
|
+
}
|
|
81
|
+
return node.loc;
|
|
82
|
+
}
|
|
83
|
+
function collectDeclarationExports(declaration, allowPrimitiveExports) {
|
|
84
|
+
if (!declaration) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
switch (declaration.type) {
|
|
88
|
+
case 'VariableDeclaration':
|
|
89
|
+
return declaration.declarations.flatMap(function (declarator) {
|
|
90
|
+
if (allowPrimitiveExports && isPrimitiveLiteral(declarator)) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return collectBindingExports(declarator.id);
|
|
94
|
+
});
|
|
95
|
+
case 'FunctionDeclaration':
|
|
96
|
+
case 'ClassDeclaration':
|
|
97
|
+
return declaration.id ? [{
|
|
98
|
+
name: declaration.id.name,
|
|
99
|
+
loc: declaration.id.loc
|
|
100
|
+
}] : [];
|
|
101
|
+
case 'TSEnumDeclaration':
|
|
102
|
+
return [{
|
|
103
|
+
name: declaration.id.name,
|
|
104
|
+
loc: declaration.id.loc
|
|
105
|
+
}];
|
|
106
|
+
case 'TSInterfaceDeclaration':
|
|
107
|
+
case 'TSTypeAliasDeclaration':
|
|
108
|
+
return [];
|
|
109
|
+
default:
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function collectNamedSpecifierExports(node) {
|
|
114
|
+
if (node.exportKind === 'type') {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
return node.specifiers.flatMap(function (specifier) {
|
|
118
|
+
if (specifier.type !== 'ExportSpecifier' || specifier.exportKind === 'type') {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
var exportedName = getPropertyName(specifier.exported);
|
|
122
|
+
return exportedName ? [{
|
|
123
|
+
name: exportedName,
|
|
124
|
+
loc: specifier.exported.loc
|
|
125
|
+
}] : [];
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
var rule = {
|
|
129
|
+
meta: {
|
|
130
|
+
type: 'suggestion',
|
|
131
|
+
docs: {
|
|
132
|
+
description: 'Disallow more than one local value export per file.',
|
|
133
|
+
category: 'Best Practices',
|
|
134
|
+
recommended: false
|
|
135
|
+
},
|
|
136
|
+
schema: [{
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
allowPrimitiveExports: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
description: 'When true, primitive value exports (strings, numbers, booleans, null, undefined, and template literals) are ignored when counting local value exports.'
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
additionalProperties: false
|
|
145
|
+
}],
|
|
146
|
+
messages: {
|
|
147
|
+
multipleValueExports: 'This file exports {{count}} local values ({{names}}). Keep one value export per file https://hello.atlassian.net/wiki/spaces/DevInfra/pages/6809881812/One+Export+Per+File'
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
create: function create(context) {
|
|
151
|
+
var _, _options$allowPrimiti;
|
|
152
|
+
var options = (_ = context.options[0]) !== null && _ !== void 0 ? _ : {};
|
|
153
|
+
var allowPrimitiveExports = (_options$allowPrimiti = options.allowPrimitiveExports) !== null && _options$allowPrimiti !== void 0 ? _options$allowPrimiti : false;
|
|
154
|
+
var valueExports = [];
|
|
155
|
+
return {
|
|
156
|
+
ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
|
|
157
|
+
var exportNode = node;
|
|
158
|
+
valueExports.push({
|
|
159
|
+
name: getDefaultExportName(exportNode),
|
|
160
|
+
loc: getDefaultExportLoc(exportNode)
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
ExportNamedDeclaration: function ExportNamedDeclaration(node) {
|
|
164
|
+
var exportNode = node;
|
|
165
|
+
|
|
166
|
+
// Re-export-only barrel syntax is intentionally ignored.
|
|
167
|
+
if (exportNode.source) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
valueExports.push.apply(valueExports, _toConsumableArray(collectDeclarationExports(exportNode.declaration, allowPrimitiveExports)));
|
|
171
|
+
valueExports.push.apply(valueExports, _toConsumableArray(collectNamedSpecifierExports(exportNode)));
|
|
172
|
+
},
|
|
173
|
+
'Program:exit': function ProgramExit(node) {
|
|
174
|
+
if (valueExports.length <= 1) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
var sampleNames = valueExports.slice(0, 5).map(function (valueExport) {
|
|
178
|
+
return valueExport.name;
|
|
179
|
+
}).join(', ');
|
|
180
|
+
var names = valueExports.length > 5 ? "".concat(sampleNames, ", and ").concat(valueExports.length - 5, " more") : sampleNames;
|
|
181
|
+
valueExports.forEach(function (valueExport) {
|
|
182
|
+
context.report({
|
|
183
|
+
node: node,
|
|
184
|
+
loc: valueExport.loc,
|
|
185
|
+
messageId: 'multipleValueExports',
|
|
186
|
+
data: {
|
|
187
|
+
count: String(valueExports.length),
|
|
188
|
+
names: names
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
export default rule;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ declare const rules: {
|
|
|
36
36
|
'no-jest-mock-barrel-files': Rule.RuleModule;
|
|
37
37
|
'no-relative-barrel-file-imports': Rule.RuleModule;
|
|
38
38
|
'no-conversation-assistant-barrel-imports': Rule.RuleModule;
|
|
39
|
+
'one-value-export-per-file': Rule.RuleModule;
|
|
39
40
|
'visit-example-type-import-required': Rule.RuleModule;
|
|
40
41
|
'no-xcss-in-cx': Rule.RuleModule;
|
|
41
42
|
'editor-example-type-import-required': Rule.RuleModule;
|
|
@@ -85,6 +86,7 @@ declare const plugin: {
|
|
|
85
86
|
'no-jest-mock-barrel-files': Rule.RuleModule;
|
|
86
87
|
'no-relative-barrel-file-imports': Rule.RuleModule;
|
|
87
88
|
'no-conversation-assistant-barrel-imports': Rule.RuleModule;
|
|
89
|
+
'one-value-export-per-file': Rule.RuleModule;
|
|
88
90
|
'visit-example-type-import-required': Rule.RuleModule;
|
|
89
91
|
'editor-example-type-import-required': Rule.RuleModule;
|
|
90
92
|
'ensure-use-sync-external-store-server-snapshot': Rule.RuleModule;
|
|
@@ -36,6 +36,7 @@ declare const rules: {
|
|
|
36
36
|
'no-jest-mock-barrel-files': Rule.RuleModule;
|
|
37
37
|
'no-relative-barrel-file-imports': Rule.RuleModule;
|
|
38
38
|
'no-conversation-assistant-barrel-imports': Rule.RuleModule;
|
|
39
|
+
'one-value-export-per-file': Rule.RuleModule;
|
|
39
40
|
'visit-example-type-import-required': Rule.RuleModule;
|
|
40
41
|
'no-xcss-in-cx': Rule.RuleModule;
|
|
41
42
|
'editor-example-type-import-required': Rule.RuleModule;
|
|
@@ -85,6 +86,7 @@ declare const plugin: {
|
|
|
85
86
|
'no-jest-mock-barrel-files': Rule.RuleModule;
|
|
86
87
|
'no-relative-barrel-file-imports': Rule.RuleModule;
|
|
87
88
|
'no-conversation-assistant-barrel-imports': Rule.RuleModule;
|
|
89
|
+
'one-value-export-per-file': Rule.RuleModule;
|
|
88
90
|
'visit-example-type-import-required': Rule.RuleModule;
|
|
89
91
|
'editor-example-type-import-required': Rule.RuleModule;
|
|
90
92
|
'ensure-use-sync-external-store-server-snapshot': Rule.RuleModule;
|
package/package.json
CHANGED