@atlaskit/eslint-plugin-platform 2.9.2 → 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 +15 -0
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/rules/compiled/expand-motion-shorthand/index.js +281 -0
- package/dist/cjs/rules/compiled/use-motion-token-values/index.js +506 -0
- package/dist/cjs/rules/editor-example-type-import-required/index.js +321 -0
- package/dist/cjs/rules/import/one-value-export-per-file/index.js +203 -0
- package/dist/es2019/index.js +11 -1
- package/dist/es2019/rules/compiled/expand-motion-shorthand/index.js +239 -0
- package/dist/es2019/rules/compiled/use-motion-token-values/index.js +444 -0
- package/dist/es2019/rules/editor-example-type-import-required/index.js +286 -0
- package/dist/es2019/rules/import/one-value-export-per-file/index.js +191 -0
- package/dist/esm/index.js +11 -1
- package/dist/esm/rules/compiled/expand-motion-shorthand/index.js +275 -0
- package/dist/esm/rules/compiled/use-motion-token-values/index.js +499 -0
- package/dist/esm/rules/editor-example-type-import-required/index.js +314 -0
- package/dist/esm/rules/import/one-value-export-per-file/index.js +196 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types/rules/compiled/use-motion-token-values/index.d.ts +3 -0
- package/dist/types/rules/editor-example-type-import-required/index.d.ts +4 -0
- package/dist/types/rules/import/one-value-export-per-file/index.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +6 -0
- package/dist/types-ts4.5/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/compiled/use-motion-token-values/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/editor-example-type-import-required/index.d.ts +4 -0
- package/dist/types-ts4.5/rules/import/one-value-export-per-file/index.d.ts +3 -0
- package/package.json +2 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = exports.RULE_NAME = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var _utils = require("@typescript-eslint/utils");
|
|
12
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
13
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
14
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
15
|
+
var RULE_NAME = exports.RULE_NAME = 'editor-example-type-import-required';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Spec files that are excluded from this rule because they don't use visitExample
|
|
19
|
+
* or have their own test harness that doesn't follow the exampleName fixture pattern.
|
|
20
|
+
*
|
|
21
|
+
* Paths are matched as suffixes of the file path (platform-relative).
|
|
22
|
+
*/
|
|
23
|
+
var EXCLUDED_SPEC_FILES = [
|
|
24
|
+
// Meta-tests for the testing infrastructure itself
|
|
25
|
+
'build/test-tooling/integration-testing/src/examples/__tests__/playwright/example.spec.ts', 'build/test-tooling/integration-testing/src/matchers/__tests__/playwright/to-have-height.spec.ts', 'build/test-tooling/integration-testing/src/matchers/__tests__/playwright/to-have-width.spec.ts',
|
|
26
|
+
// Tests the a11y decorator itself, no visitExample
|
|
27
|
+
'packages/accessibility/axe-integration/a11y-playwright-testing/src/auto-a11y-setup/__tests__/playwright/skip-decorator.spec.ts',
|
|
28
|
+
// Stub test (expect(true).toBe(true)), no visitExample
|
|
29
|
+
'packages/ai-mate/rovo-content-bridge-api/__tests__/playwright/index.spec.tsx',
|
|
30
|
+
// Page-object's visitExample call carries the typeof import(...) generic,
|
|
31
|
+
// so the typed example reference lives in _helpers/page-object.ts rather
|
|
32
|
+
// than in test.use({ exampleName }) in the spec itself.
|
|
33
|
+
'packages/navigation/atlassian-switcher/src/__tests__/playwright/navigate-child-item.spec.ts', 'packages/navigation/atlassian-switcher/src/__tests__/playwright/navigate-link-item.spec.ts', 'packages/navigation/atlassian-switcher/src/__tests__/playwright/navigate-product-item.spec.ts',
|
|
34
|
+
// Spec runs through a page-object (pages/generic-form-renderer.ts) whose
|
|
35
|
+
// visitExample call already carries the typeof import(...) generic. The
|
|
36
|
+
// typed reference therefore lives in the colocated page-object, not in
|
|
37
|
+
// test.use({ exampleName }) in the spec.
|
|
38
|
+
'packages/proforma/proforma-common-core/__tests__/playwright/json-test-cases.spec.ts', 'packages/proforma/proforma-form-renderer/__tests__/playwright/json-test-cases.spec.ts',
|
|
39
|
+
// Spec runs through a page-object that still uses raw page.goto() against
|
|
40
|
+
// /examples.html. Migrating these requires reworking the page-object to
|
|
41
|
+
// route through visitExample<typeof import(...)>(...).
|
|
42
|
+
'packages/proforma/proforma-form-list/__tests__/playwright/form-list.spec.ts', 'packages/proforma/proforma-form-renderer/__tests__/playwright/form-renderer.spec.ts', 'packages/proforma/proforma-translations-editor/__tests__/playwright/translations-editor-with-form.spec.ts',
|
|
43
|
+
// Tests the website itself, not examples
|
|
44
|
+
'website/src/__tests__/playwright/examples.spec.ts', 'website/src/__tests__/playwright/home.spec.ts',
|
|
45
|
+
// react-ufo: uses an `examplePage: string` fixture where the name (e.g. 'basic') is
|
|
46
|
+
// resolved to the example file internally by visitExample — the name does not match
|
|
47
|
+
// the file name (e.g. '01-basic.tsx'), so a typeof import assertion is not possible
|
|
48
|
+
// without refactoring the fixture to use keyof typeof import directly. Specs that
|
|
49
|
+
// happen to also use the inline `visitExample<typeof import(...)>` pattern alongside
|
|
50
|
+
// the fixture pass via the file-level typeof import check above and don't appear here.
|
|
51
|
+
'packages/react-ufo/atlaskit/__tests__/playwright/apply-segments-threshold.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/bad-replacement-node.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/base-10-sections.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/base-100-sections.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/base-3-sections-ssr-timings.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/base-3-sections-unmount.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/css-display-contents.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/custom-cohort-data.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/custom-data.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/data-vc-ignore-if-no-layout-shift.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/full-pixel-horizontal.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/full-pixel.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/fy25_02.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/hold.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/interactions-responsiveness.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/interactions-unknown-element.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/interactions-vc.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/is-opened-in-background.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/is-tab-throttled.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/metric-variants.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/minor-interactions.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/non-visual-style.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/page-visibility-hidden-timestamp.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/payload-integrity.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/post-interaction-late-holds.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/post-interaction-log-always-send.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/replacement-node.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/revisions.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/same-attribute-value-mutation.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/speed-index.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/ssr-placeholder-v3.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/terminal-error.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/third-party-segment-extra-metrics.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/third-party-segment-iframe.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/third-party-segment-timings.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/third-party-segment.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/transition-vc.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/ttai.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/ufo-blindspot-watchdog.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/ufo-errors.spec.ts', 'packages/react-ufo/atlaskit/__tests__/playwright/vc-dirty.spec.ts',
|
|
52
|
+
// editor-performance-metrics: same pattern as react-ufo — uses an `examplePage: string` fixture
|
|
53
|
+
// where the name resolves internally and does not match the example file name.
|
|
54
|
+
'packages/editor/editor-performance-metrics/__tests__/playwright/basic-editor-ttai.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/basic-react-app.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/latency-track.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/ttai-timers.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-attribute-change.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-element-moving.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-moving-node.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-placeholder.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-react-remounting.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next-track-user-events.spec.ts', 'packages/editor/editor-performance-metrics/__tests__/playwright/vc-next.spec.ts',
|
|
55
|
+
// generative-ai-modal: the example name is passed dynamically at examplePage.goto({ example: '...' })
|
|
56
|
+
// time rather than via test.use(), so a static typeof import assertion in the spec is not possible.
|
|
57
|
+
'packages/editor/generative-ai-modal/src/ui/screens/Preview/__tests__/playwright/tab-navigation.spec.ts'];
|
|
58
|
+
function isExcluded(filename) {
|
|
59
|
+
var normalised = filename.replace(/\\/g, '/');
|
|
60
|
+
return EXCLUDED_SPEC_FILES.some(function (excluded) {
|
|
61
|
+
return normalised.endsWith(excluded);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
var messages = {
|
|
65
|
+
missingExampleName: 'Playwright spec files must include exampleName with a ' + 'typeof import type assertion in test.use(). ' + "Add: exampleName: 'testing' as keyof typeof import('../../../examples/testing.tsx') ",
|
|
66
|
+
missingTypeAssertion: 'exampleName must include a typeof import type assertion for the static import graph. ' + "Use: exampleName: '{{ value }}' as keyof typeof import('{{ expectedPath }}') ",
|
|
67
|
+
pathMismatch: 'The import path "{{ importPath }}" does not resolve to the expected example file ' + "for exampleName '{{ exampleName }}'. Expected: {{ expectedPath }}"
|
|
68
|
+
};
|
|
69
|
+
function isTargetFile(filename) {
|
|
70
|
+
return (filename.endsWith('.spec.tsx') || filename.endsWith('.spec.ts')) && !isExcluded(filename);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolves the example file path from the spec file's location and the example name.
|
|
75
|
+
* Editor specs follow: packages/{groupId}/{packageId}/src/__tests__/playwright/*.spec.ts
|
|
76
|
+
* Examples live at: packages/{groupId}/{packageId}/examples/{exampleName}.tsx
|
|
77
|
+
*/
|
|
78
|
+
function resolveExamplePath(testFilePath, exampleName) {
|
|
79
|
+
var testFileDir = _path.default.dirname(testFilePath);
|
|
80
|
+
var segments = testFileDir.split(_path.default.sep);
|
|
81
|
+
var packagesIndex = segments.findIndex(function (seg) {
|
|
82
|
+
return seg === 'packages';
|
|
83
|
+
});
|
|
84
|
+
if (packagesIndex === -1) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
var groupId = segments[packagesIndex + 1];
|
|
88
|
+
var packageId = segments[packagesIndex + 2];
|
|
89
|
+
if (!groupId || !packageId) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
var basePath = _path.default.isAbsolute(testFilePath) ? _path.default.resolve.apply(_path.default, ['/'].concat((0, _toConsumableArray2.default)(segments.slice(0, packagesIndex + 1)))) : _path.default.resolve.apply(_path.default, [process.cwd()].concat((0, _toConsumableArray2.default)(segments.slice(0, packagesIndex + 1))));
|
|
93
|
+
var examplesDir = _path.default.resolve(basePath, groupId, packageId, 'examples');
|
|
94
|
+
var candidateRe = new RegExp("^(?:\\d+-)?".concat(exampleName, "(?:\\.examples?)?\\.tsx$"));
|
|
95
|
+
try {
|
|
96
|
+
var match = _fs.default.readdirSync(examplesDir).find(function (f) {
|
|
97
|
+
return candidateRe.test(f);
|
|
98
|
+
});
|
|
99
|
+
if (match) {
|
|
100
|
+
return _path.default.resolve(examplesDir, match);
|
|
101
|
+
}
|
|
102
|
+
} catch (_unused) {
|
|
103
|
+
// Directory doesn't exist (e.g. test environments)
|
|
104
|
+
}
|
|
105
|
+
return _path.default.resolve(examplesDir, "".concat(exampleName, ".tsx"));
|
|
106
|
+
}
|
|
107
|
+
function computeRelativeImportPath(fromFile, toFile) {
|
|
108
|
+
var fromDir = _path.default.dirname(fromFile);
|
|
109
|
+
var relativePath = _path.default.relative(fromDir, toFile);
|
|
110
|
+
relativePath = relativePath.replace(/\\/g, '/');
|
|
111
|
+
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
112
|
+
relativePath = "./".concat(relativePath);
|
|
113
|
+
}
|
|
114
|
+
return relativePath;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a property value has a `keyof typeof import(...)` type assertion.
|
|
119
|
+
* Handles both:
|
|
120
|
+
* 'name' as keyof typeof import('...')
|
|
121
|
+
*/
|
|
122
|
+
function extractTypeofImportPath(node) {
|
|
123
|
+
if (node.type !== _utils.AST_NODE_TYPES.TSAsExpression) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
var typeAnnotation = node.typeAnnotation;
|
|
127
|
+
if (typeAnnotation.type === _utils.AST_NODE_TYPES.TSTypeOperator && typeAnnotation.operator === 'keyof') {
|
|
128
|
+
return extractFromTypeQuery(typeAnnotation.typeAnnotation);
|
|
129
|
+
}
|
|
130
|
+
if (typeAnnotation.type === _utils.AST_NODE_TYPES.TSUnionType) {
|
|
131
|
+
var _iterator = _createForOfIteratorHelper(typeAnnotation.types),
|
|
132
|
+
_step;
|
|
133
|
+
try {
|
|
134
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
135
|
+
var member = _step.value;
|
|
136
|
+
if (member.type === _utils.AST_NODE_TYPES.TSTypeOperator && member.operator === 'keyof') {
|
|
137
|
+
var result = extractFromTypeQuery(member.typeAnnotation);
|
|
138
|
+
if (result) {
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
_iterator.e(err);
|
|
145
|
+
} finally {
|
|
146
|
+
_iterator.f();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
function extractFromTypeQuery(node) {
|
|
152
|
+
if (!node || node.type !== _utils.AST_NODE_TYPES.TSTypeQuery) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
var exprName = node.exprName;
|
|
156
|
+
if (exprName.type !== _utils.AST_NODE_TYPES.TSImportType) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
var argument = exprName.argument;
|
|
160
|
+
if (argument.type === _utils.AST_NODE_TYPES.TSLiteralType && argument.literal.type === _utils.AST_NODE_TYPES.Literal && typeof argument.literal.value === 'string') {
|
|
161
|
+
return argument.literal.value;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Extract the string value from a property value, ignoring type assertions.
|
|
168
|
+
*/
|
|
169
|
+
function getStringValue(node) {
|
|
170
|
+
if (node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'string') {
|
|
171
|
+
return node.value;
|
|
172
|
+
}
|
|
173
|
+
if (node.type === _utils.AST_NODE_TYPES.TSAsExpression) {
|
|
174
|
+
return getStringValue(node.expression);
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
var rule = {
|
|
179
|
+
meta: {
|
|
180
|
+
type: 'problem',
|
|
181
|
+
docs: {
|
|
182
|
+
description: 'Ensures that editor spec files using @af/editor-libra include exampleName with a ' + 'typeof import type assertion in test.use() for the static import graph (factsMap).'
|
|
183
|
+
},
|
|
184
|
+
fixable: 'code',
|
|
185
|
+
messages: messages,
|
|
186
|
+
schema: []
|
|
187
|
+
},
|
|
188
|
+
create: function create(context) {
|
|
189
|
+
var filename = context.filename;
|
|
190
|
+
if (!isTargetFile(filename)) {
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
'Program:exit': function ProgramExit(estreeNode) {
|
|
195
|
+
var program = estreeNode;
|
|
196
|
+
|
|
197
|
+
// Single AST walk: collect all test.use() calls AND detect whether the
|
|
198
|
+
// file contains any `typeof import('...')` reference at all (a TSImportType
|
|
199
|
+
// node). Either signal is sufficient evidence that the spec ties at least
|
|
200
|
+
// one example file into its TypeScript import graph.
|
|
201
|
+
var testUseCalls = [];
|
|
202
|
+
var hasAnyTypeofImport = false;
|
|
203
|
+
var visited = new Set();
|
|
204
|
+
var queue = [program];
|
|
205
|
+
while (queue.length > 0) {
|
|
206
|
+
var node = queue.shift();
|
|
207
|
+
if (visited.has(node)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
visited.add(node);
|
|
211
|
+
if (node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === _utils.AST_NODE_TYPES.Identifier && node.callee.property.name === 'use' && node.arguments.length > 0 && node.arguments[0].type === _utils.AST_NODE_TYPES.ObjectExpression) {
|
|
212
|
+
testUseCalls.push(node);
|
|
213
|
+
}
|
|
214
|
+
if (node.type === _utils.AST_NODE_TYPES.TSImportType) {
|
|
215
|
+
hasAnyTypeofImport = true;
|
|
216
|
+
}
|
|
217
|
+
for (var _i = 0, _Object$keys = Object.keys(node); _i < _Object$keys.length; _i++) {
|
|
218
|
+
var key = _Object$keys[_i];
|
|
219
|
+
if (key === 'parent') {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
var child = node[key];
|
|
223
|
+
if (Array.isArray(child)) {
|
|
224
|
+
var _iterator2 = _createForOfIteratorHelper(child),
|
|
225
|
+
_step2;
|
|
226
|
+
try {
|
|
227
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
228
|
+
var item = _step2.value;
|
|
229
|
+
if (item && typeof item.type === 'string') {
|
|
230
|
+
queue.push(item);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
_iterator2.e(err);
|
|
235
|
+
} finally {
|
|
236
|
+
_iterator2.f();
|
|
237
|
+
}
|
|
238
|
+
} else if (child && typeof child.type === 'string') {
|
|
239
|
+
queue.push(child);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Any `typeof import('...')` anywhere in the spec — including the
|
|
245
|
+
// `page.visitExample<typeof import('...')>(...)` and
|
|
246
|
+
// `page.visitMockedExample<typeof import('...')>(...)` patterns used
|
|
247
|
+
// outside of test.use() — satisfies the same goal as the canonical
|
|
248
|
+
// `test.use({ exampleName: '...' as keyof typeof import('...') })`
|
|
249
|
+
// pattern: the example file is referenced from the spec's TypeScript
|
|
250
|
+
// import graph.
|
|
251
|
+
if (hasAnyTypeofImport) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Check if any test.use() call anywhere in the file has exampleName with a typeof import assertion
|
|
256
|
+
var fileHasExampleName = testUseCalls.some(function (call) {
|
|
257
|
+
var obj = call.arguments[0];
|
|
258
|
+
return obj.properties.some(function (prop) {
|
|
259
|
+
if (prop.type !== _utils.AST_NODE_TYPES.Property || prop.key.type !== _utils.AST_NODE_TYPES.Identifier || prop.key.name !== 'exampleName') {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
return extractTypeofImportPath(prop.value) !== null;
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
if (fileHasExampleName) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Find the first test.use() call that has exampleName without the typeof import assertion
|
|
270
|
+
// (if any), otherwise use the first test.use() call
|
|
271
|
+
var targetCall = testUseCalls[0];
|
|
272
|
+
var existingExampleNameProp = null;
|
|
273
|
+
for (var _i2 = 0, _testUseCalls = testUseCalls; _i2 < _testUseCalls.length; _i2++) {
|
|
274
|
+
var call = _testUseCalls[_i2];
|
|
275
|
+
var obj = call.arguments[0];
|
|
276
|
+
var prop = obj.properties.find(function (p) {
|
|
277
|
+
return p.type === _utils.AST_NODE_TYPES.Property && p.key.type === _utils.AST_NODE_TYPES.Identifier && p.key.name === 'exampleName';
|
|
278
|
+
});
|
|
279
|
+
if (prop) {
|
|
280
|
+
targetCall = call;
|
|
281
|
+
existingExampleNameProp = prop;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (!targetCall) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
var objectArg = targetCall.arguments[0];
|
|
289
|
+
|
|
290
|
+
// Determine the example name to use for the import path
|
|
291
|
+
var exampleNameValue = existingExampleNameProp ? getStringValue(existingExampleNameProp.value) : null;
|
|
292
|
+
var defaultName = exampleNameValue !== null && exampleNameValue !== void 0 ? exampleNameValue : 'testing';
|
|
293
|
+
var examplePath = resolveExamplePath(filename, defaultName);
|
|
294
|
+
if (!examplePath) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
var importPath = computeRelativeImportPath(filename, examplePath);
|
|
298
|
+
context.report({
|
|
299
|
+
node: targetCall,
|
|
300
|
+
messageId: 'missingExampleName',
|
|
301
|
+
fix: function fix(fixer) {
|
|
302
|
+
// If exampleName exists but lacks typeof import, replace its value
|
|
303
|
+
if (existingExampleNameProp) {
|
|
304
|
+
return fixer.replaceText(existingExampleNameProp.value, "'".concat(defaultName, "' as keyof typeof import('").concat(importPath, "') "));
|
|
305
|
+
}
|
|
306
|
+
// Otherwise insert exampleName as the first property
|
|
307
|
+
var firstProp = objectArg.properties[0];
|
|
308
|
+
if (!firstProp) {
|
|
309
|
+
return fixer.replaceText(targetCall.arguments[0], "{\n\texampleName: '".concat(defaultName, "' as keyof typeof import('").concat(importPath, "'),\n}"));
|
|
310
|
+
}
|
|
311
|
+
var sourceCode = context.sourceCode;
|
|
312
|
+
var token = sourceCode.getFirstToken(firstProp);
|
|
313
|
+
var indent = token ? '\t'.repeat(token.loc.start.column) : '\t';
|
|
314
|
+
return fixer.insertTextBefore(firstProp, "exampleName: '".concat(defaultName, "' as keyof typeof import('").concat(importPath, "') ,\n").concat(indent));
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
var _default = exports.default = rule;
|
|
@@ -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
|
@@ -26,6 +26,8 @@ import validGateName from './rules/feature-gating/valid-gate-name';
|
|
|
26
26
|
import expandBackgroundShorthand from './rules/compiled/expand-background-shorthand';
|
|
27
27
|
import expandSpacingShorthand from './rules/compiled/expand-spacing-shorthand';
|
|
28
28
|
import noCssPropInObjectSpread from './rules/compiled/no-css-prop-in-object-spread';
|
|
29
|
+
import useMotionTokenValues from './rules/compiled/use-motion-token-values';
|
|
30
|
+
import expandMotionShorthand from './rules/compiled/expand-motion-shorthand';
|
|
29
31
|
import noSparseCheckout from './rules/no-sparse-checkout';
|
|
30
32
|
import noDirectDocumentUsage from './rules/no-direct-document-usage';
|
|
31
33
|
import noSetImmediate from './rules/no-set-immediate';
|
|
@@ -36,7 +38,9 @@ import noBarrelEntryJestMock from './rules/import/no-barrel-entry-jest-mock';
|
|
|
36
38
|
import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
|
|
37
39
|
import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-imports';
|
|
38
40
|
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
41
|
+
import oneValueExportPerFile from './rules/import/one-value-export-per-file';
|
|
39
42
|
import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
|
|
43
|
+
import editorExampleTypeImportRequired from './rules/editor-example-type-import-required';
|
|
40
44
|
import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
|
|
41
45
|
import noXcssInCx from './rules/no-xcss-in-cx';
|
|
42
46
|
import { join, normalize } from 'node:path';
|
|
@@ -95,9 +99,13 @@ const rules = {
|
|
|
95
99
|
'no-jest-mock-barrel-files': noJestMockBarrelFiles,
|
|
96
100
|
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
97
101
|
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
|
|
102
|
+
'one-value-export-per-file': oneValueExportPerFile,
|
|
98
103
|
'visit-example-type-import-required': visitExampleTypeImportRequired,
|
|
99
104
|
'no-xcss-in-cx': noXcssInCx,
|
|
100
|
-
'
|
|
105
|
+
'editor-example-type-import-required': editorExampleTypeImportRequired,
|
|
106
|
+
'ensure-use-sync-external-store-server-snapshot': ensureUseSyncExternalStoreServerSnapshot,
|
|
107
|
+
'use-motion-token-values': useMotionTokenValues,
|
|
108
|
+
'expand-motion-shorthand': expandMotionShorthand
|
|
101
109
|
};
|
|
102
110
|
const commonConfig = {
|
|
103
111
|
'@atlaskit/platform/ensure-test-runner-arguments': 'error',
|
|
@@ -115,6 +123,8 @@ const commonConfig = {
|
|
|
115
123
|
'@atlaskit/platform/expand-background-shorthand': 'error',
|
|
116
124
|
'@atlaskit/platform/expand-spacing-shorthand': 'error',
|
|
117
125
|
'@atlaskit/platform/no-css-prop-in-object-spread': 'error',
|
|
126
|
+
'@atlaskit/platform/use-motion-token-values': 'warn',
|
|
127
|
+
'@atlaskit/platform/expand-motion-shorthand': 'warn',
|
|
118
128
|
'@compiled/jsx-pragma': ['error', {
|
|
119
129
|
importSources: ['@atlaskit/css'],
|
|
120
130
|
onlyRunIfImportingCompiled: true,
|