@atlaskit/eslint-plugin-platform 2.9.1 → 2.9.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 +18 -0
- package/dist/cjs/index.js +15 -3
- package/dist/cjs/rules/compiled/expand-motion-shorthand/index.js +281 -0
- package/dist/cjs/rules/compiled/no-css-prop-in-object-spread/index.js +162 -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/no-xcss-in-cx/index.js +221 -0
- package/dist/cjs/rules/visit-example-type-import-required/index.js +23 -13
- package/dist/es2019/index.js +15 -3
- package/dist/es2019/rules/compiled/expand-motion-shorthand/index.js +239 -0
- package/dist/es2019/rules/compiled/no-css-prop-in-object-spread/index.js +136 -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/no-xcss-in-cx/index.js +187 -0
- package/dist/es2019/rules/visit-example-type-import-required/index.js +23 -14
- package/dist/esm/index.js +15 -3
- package/dist/esm/rules/compiled/expand-motion-shorthand/index.js +275 -0
- package/dist/esm/rules/compiled/no-css-prop-in-object-spread/index.js +156 -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/no-xcss-in-cx/index.js +216 -0
- package/dist/esm/rules/visit-example-type-import-required/index.js +23 -13
- package/dist/types/index.d.ts +282 -243
- package/dist/types/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types/rules/compiled/no-css-prop-in-object-spread/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/no-xcss-in-cx/index.d.ts +31 -0
- package/dist/types-ts4.5/index.d.ts +226 -211
- package/dist/types-ts4.5/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/compiled/no-css-prop-in-object-spread/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/no-xcss-in-cx/index.d.ts +31 -0
- package/package.json +2 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
4
|
+
export const RULE_NAME = 'editor-example-type-import-required';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Spec files that are excluded from this rule because they don't use visitExample
|
|
8
|
+
* or have their own test harness that doesn't follow the exampleName fixture pattern.
|
|
9
|
+
*
|
|
10
|
+
* Paths are matched as suffixes of the file path (platform-relative).
|
|
11
|
+
*/
|
|
12
|
+
const EXCLUDED_SPEC_FILES = [
|
|
13
|
+
// Meta-tests for the testing infrastructure itself
|
|
14
|
+
'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',
|
|
15
|
+
// Tests the a11y decorator itself, no visitExample
|
|
16
|
+
'packages/accessibility/axe-integration/a11y-playwright-testing/src/auto-a11y-setup/__tests__/playwright/skip-decorator.spec.ts',
|
|
17
|
+
// Stub test (expect(true).toBe(true)), no visitExample
|
|
18
|
+
'packages/ai-mate/rovo-content-bridge-api/__tests__/playwright/index.spec.tsx',
|
|
19
|
+
// Page-object's visitExample call carries the typeof import(...) generic,
|
|
20
|
+
// so the typed example reference lives in _helpers/page-object.ts rather
|
|
21
|
+
// than in test.use({ exampleName }) in the spec itself.
|
|
22
|
+
'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',
|
|
23
|
+
// Spec runs through a page-object (pages/generic-form-renderer.ts) whose
|
|
24
|
+
// visitExample call already carries the typeof import(...) generic. The
|
|
25
|
+
// typed reference therefore lives in the colocated page-object, not in
|
|
26
|
+
// test.use({ exampleName }) in the spec.
|
|
27
|
+
'packages/proforma/proforma-common-core/__tests__/playwright/json-test-cases.spec.ts', 'packages/proforma/proforma-form-renderer/__tests__/playwright/json-test-cases.spec.ts',
|
|
28
|
+
// Spec runs through a page-object that still uses raw page.goto() against
|
|
29
|
+
// /examples.html. Migrating these requires reworking the page-object to
|
|
30
|
+
// route through visitExample<typeof import(...)>(...).
|
|
31
|
+
'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',
|
|
32
|
+
// Tests the website itself, not examples
|
|
33
|
+
'website/src/__tests__/playwright/examples.spec.ts', 'website/src/__tests__/playwright/home.spec.ts',
|
|
34
|
+
// react-ufo: uses an `examplePage: string` fixture where the name (e.g. 'basic') is
|
|
35
|
+
// resolved to the example file internally by visitExample — the name does not match
|
|
36
|
+
// the file name (e.g. '01-basic.tsx'), so a typeof import assertion is not possible
|
|
37
|
+
// without refactoring the fixture to use keyof typeof import directly. Specs that
|
|
38
|
+
// happen to also use the inline `visitExample<typeof import(...)>` pattern alongside
|
|
39
|
+
// the fixture pass via the file-level typeof import check above and don't appear here.
|
|
40
|
+
'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',
|
|
41
|
+
// editor-performance-metrics: same pattern as react-ufo — uses an `examplePage: string` fixture
|
|
42
|
+
// where the name resolves internally and does not match the example file name.
|
|
43
|
+
'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',
|
|
44
|
+
// generative-ai-modal: the example name is passed dynamically at examplePage.goto({ example: '...' })
|
|
45
|
+
// time rather than via test.use(), so a static typeof import assertion in the spec is not possible.
|
|
46
|
+
'packages/editor/generative-ai-modal/src/ui/screens/Preview/__tests__/playwright/tab-navigation.spec.ts'];
|
|
47
|
+
function isExcluded(filename) {
|
|
48
|
+
const normalised = filename.replace(/\\/g, '/');
|
|
49
|
+
return EXCLUDED_SPEC_FILES.some(excluded => normalised.endsWith(excluded));
|
|
50
|
+
}
|
|
51
|
+
const messages = {
|
|
52
|
+
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') ",
|
|
53
|
+
missingTypeAssertion: 'exampleName must include a typeof import type assertion for the static import graph. ' + "Use: exampleName: '{{ value }}' as keyof typeof import('{{ expectedPath }}') ",
|
|
54
|
+
pathMismatch: 'The import path "{{ importPath }}" does not resolve to the expected example file ' + "for exampleName '{{ exampleName }}'. Expected: {{ expectedPath }}"
|
|
55
|
+
};
|
|
56
|
+
function isTargetFile(filename) {
|
|
57
|
+
return (filename.endsWith('.spec.tsx') || filename.endsWith('.spec.ts')) && !isExcluded(filename);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the example file path from the spec file's location and the example name.
|
|
62
|
+
* Editor specs follow: packages/{groupId}/{packageId}/src/__tests__/playwright/*.spec.ts
|
|
63
|
+
* Examples live at: packages/{groupId}/{packageId}/examples/{exampleName}.tsx
|
|
64
|
+
*/
|
|
65
|
+
function resolveExamplePath(testFilePath, exampleName) {
|
|
66
|
+
const testFileDir = path.dirname(testFilePath);
|
|
67
|
+
const segments = testFileDir.split(path.sep);
|
|
68
|
+
const packagesIndex = segments.findIndex(seg => seg === 'packages');
|
|
69
|
+
if (packagesIndex === -1) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const groupId = segments[packagesIndex + 1];
|
|
73
|
+
const packageId = segments[packagesIndex + 2];
|
|
74
|
+
if (!groupId || !packageId) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const basePath = path.isAbsolute(testFilePath) ? path.resolve('/', ...segments.slice(0, packagesIndex + 1)) : path.resolve(process.cwd(), ...segments.slice(0, packagesIndex + 1));
|
|
78
|
+
const examplesDir = path.resolve(basePath, groupId, packageId, 'examples');
|
|
79
|
+
const candidateRe = new RegExp(`^(?:\\d+-)?${exampleName}(?:\\.examples?)?\\.tsx$`);
|
|
80
|
+
try {
|
|
81
|
+
const match = fs.readdirSync(examplesDir).find(f => candidateRe.test(f));
|
|
82
|
+
if (match) {
|
|
83
|
+
return path.resolve(examplesDir, match);
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Directory doesn't exist (e.g. test environments)
|
|
87
|
+
}
|
|
88
|
+
return path.resolve(examplesDir, `${exampleName}.tsx`);
|
|
89
|
+
}
|
|
90
|
+
function computeRelativeImportPath(fromFile, toFile) {
|
|
91
|
+
const fromDir = path.dirname(fromFile);
|
|
92
|
+
let relativePath = path.relative(fromDir, toFile);
|
|
93
|
+
relativePath = relativePath.replace(/\\/g, '/');
|
|
94
|
+
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
95
|
+
relativePath = `./${relativePath}`;
|
|
96
|
+
}
|
|
97
|
+
return relativePath;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if a property value has a `keyof typeof import(...)` type assertion.
|
|
102
|
+
* Handles both:
|
|
103
|
+
* 'name' as keyof typeof import('...')
|
|
104
|
+
*/
|
|
105
|
+
function extractTypeofImportPath(node) {
|
|
106
|
+
if (node.type !== AST_NODE_TYPES.TSAsExpression) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const typeAnnotation = node.typeAnnotation;
|
|
110
|
+
if (typeAnnotation.type === AST_NODE_TYPES.TSTypeOperator && typeAnnotation.operator === 'keyof') {
|
|
111
|
+
return extractFromTypeQuery(typeAnnotation.typeAnnotation);
|
|
112
|
+
}
|
|
113
|
+
if (typeAnnotation.type === AST_NODE_TYPES.TSUnionType) {
|
|
114
|
+
for (const member of typeAnnotation.types) {
|
|
115
|
+
if (member.type === AST_NODE_TYPES.TSTypeOperator && member.operator === 'keyof') {
|
|
116
|
+
const result = extractFromTypeQuery(member.typeAnnotation);
|
|
117
|
+
if (result) {
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function extractFromTypeQuery(node) {
|
|
126
|
+
if (!node || node.type !== AST_NODE_TYPES.TSTypeQuery) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const {
|
|
130
|
+
exprName
|
|
131
|
+
} = node;
|
|
132
|
+
if (exprName.type !== AST_NODE_TYPES.TSImportType) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const {
|
|
136
|
+
argument
|
|
137
|
+
} = exprName;
|
|
138
|
+
if (argument.type === AST_NODE_TYPES.TSLiteralType && argument.literal.type === AST_NODE_TYPES.Literal && typeof argument.literal.value === 'string') {
|
|
139
|
+
return argument.literal.value;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Extract the string value from a property value, ignoring type assertions.
|
|
146
|
+
*/
|
|
147
|
+
function getStringValue(node) {
|
|
148
|
+
if (node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string') {
|
|
149
|
+
return node.value;
|
|
150
|
+
}
|
|
151
|
+
if (node.type === AST_NODE_TYPES.TSAsExpression) {
|
|
152
|
+
return getStringValue(node.expression);
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const rule = {
|
|
157
|
+
meta: {
|
|
158
|
+
type: 'problem',
|
|
159
|
+
docs: {
|
|
160
|
+
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).'
|
|
161
|
+
},
|
|
162
|
+
fixable: 'code',
|
|
163
|
+
messages,
|
|
164
|
+
schema: []
|
|
165
|
+
},
|
|
166
|
+
create(context) {
|
|
167
|
+
const filename = context.filename;
|
|
168
|
+
if (!isTargetFile(filename)) {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
'Program:exit'(estreeNode) {
|
|
173
|
+
const program = estreeNode;
|
|
174
|
+
|
|
175
|
+
// Single AST walk: collect all test.use() calls AND detect whether the
|
|
176
|
+
// file contains any `typeof import('...')` reference at all (a TSImportType
|
|
177
|
+
// node). Either signal is sufficient evidence that the spec ties at least
|
|
178
|
+
// one example file into its TypeScript import graph.
|
|
179
|
+
const testUseCalls = [];
|
|
180
|
+
let hasAnyTypeofImport = false;
|
|
181
|
+
const visited = new Set();
|
|
182
|
+
const queue = [program];
|
|
183
|
+
while (queue.length > 0) {
|
|
184
|
+
const node = queue.shift();
|
|
185
|
+
if (visited.has(node)) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
visited.add(node);
|
|
189
|
+
if (node.type === AST_NODE_TYPES.CallExpression && node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'use' && node.arguments.length > 0 && node.arguments[0].type === AST_NODE_TYPES.ObjectExpression) {
|
|
190
|
+
testUseCalls.push(node);
|
|
191
|
+
}
|
|
192
|
+
if (node.type === AST_NODE_TYPES.TSImportType) {
|
|
193
|
+
hasAnyTypeofImport = true;
|
|
194
|
+
}
|
|
195
|
+
for (const key of Object.keys(node)) {
|
|
196
|
+
if (key === 'parent') {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const child = node[key];
|
|
200
|
+
if (Array.isArray(child)) {
|
|
201
|
+
for (const item of child) {
|
|
202
|
+
if (item && typeof item.type === 'string') {
|
|
203
|
+
queue.push(item);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} else if (child && typeof child.type === 'string') {
|
|
207
|
+
queue.push(child);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Any `typeof import('...')` anywhere in the spec — including the
|
|
213
|
+
// `page.visitExample<typeof import('...')>(...)` and
|
|
214
|
+
// `page.visitMockedExample<typeof import('...')>(...)` patterns used
|
|
215
|
+
// outside of test.use() — satisfies the same goal as the canonical
|
|
216
|
+
// `test.use({ exampleName: '...' as keyof typeof import('...') })`
|
|
217
|
+
// pattern: the example file is referenced from the spec's TypeScript
|
|
218
|
+
// import graph.
|
|
219
|
+
if (hasAnyTypeofImport) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check if any test.use() call anywhere in the file has exampleName with a typeof import assertion
|
|
224
|
+
const fileHasExampleName = testUseCalls.some(call => {
|
|
225
|
+
const obj = call.arguments[0];
|
|
226
|
+
return obj.properties.some(prop => {
|
|
227
|
+
if (prop.type !== AST_NODE_TYPES.Property || prop.key.type !== AST_NODE_TYPES.Identifier || prop.key.name !== 'exampleName') {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return extractTypeofImportPath(prop.value) !== null;
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
if (fileHasExampleName) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Find the first test.use() call that has exampleName without the typeof import assertion
|
|
238
|
+
// (if any), otherwise use the first test.use() call
|
|
239
|
+
let targetCall = testUseCalls[0];
|
|
240
|
+
let existingExampleNameProp = null;
|
|
241
|
+
for (const call of testUseCalls) {
|
|
242
|
+
const obj = call.arguments[0];
|
|
243
|
+
const prop = obj.properties.find(p => p.type === AST_NODE_TYPES.Property && p.key.type === AST_NODE_TYPES.Identifier && p.key.name === 'exampleName');
|
|
244
|
+
if (prop) {
|
|
245
|
+
targetCall = call;
|
|
246
|
+
existingExampleNameProp = prop;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!targetCall) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const objectArg = targetCall.arguments[0];
|
|
254
|
+
|
|
255
|
+
// Determine the example name to use for the import path
|
|
256
|
+
const exampleNameValue = existingExampleNameProp ? getStringValue(existingExampleNameProp.value) : null;
|
|
257
|
+
const defaultName = exampleNameValue !== null && exampleNameValue !== void 0 ? exampleNameValue : 'testing';
|
|
258
|
+
const examplePath = resolveExamplePath(filename, defaultName);
|
|
259
|
+
if (!examplePath) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const importPath = computeRelativeImportPath(filename, examplePath);
|
|
263
|
+
context.report({
|
|
264
|
+
node: targetCall,
|
|
265
|
+
messageId: 'missingExampleName',
|
|
266
|
+
fix(fixer) {
|
|
267
|
+
// If exampleName exists but lacks typeof import, replace its value
|
|
268
|
+
if (existingExampleNameProp) {
|
|
269
|
+
return fixer.replaceText(existingExampleNameProp.value, `'${defaultName}' as keyof typeof import('${importPath}') `);
|
|
270
|
+
}
|
|
271
|
+
// Otherwise insert exampleName as the first property
|
|
272
|
+
const firstProp = objectArg.properties[0];
|
|
273
|
+
if (!firstProp) {
|
|
274
|
+
return fixer.replaceText(targetCall.arguments[0], `{\n\texampleName: '${defaultName}' as keyof typeof import('${importPath}'),\n}`);
|
|
275
|
+
}
|
|
276
|
+
const sourceCode = context.sourceCode;
|
|
277
|
+
const token = sourceCode.getFirstToken(firstProp);
|
|
278
|
+
const indent = token ? '\t'.repeat(token.loc.start.column) : '\t';
|
|
279
|
+
return fixer.insertTextBefore(firstProp, `exampleName: '${defaultName}' as keyof typeof import('${importPath}') ,\n${indent}`);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
export default rule;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { getImportSources, isCxFunction, isXcss } from '@atlaskit/eslint-utils/is-supported-import';
|
|
2
|
+
import { getScope } from '../util/context-compat';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Disallows passing xcss() results into cx() when used in an xcss prop.
|
|
6
|
+
*
|
|
7
|
+
* xcss() from @atlaskit/primitives and cx() from @atlaskit/css / @compiled/react
|
|
8
|
+
* are incompatible — xcss() produces an opaque StyleRule object, while cx()
|
|
9
|
+
* expects Compiled atomic class name strings. Mixing them causes runtime errors.
|
|
10
|
+
* xcss() results must never be passed to cx(), whether inline or pre-defined.
|
|
11
|
+
*
|
|
12
|
+
* ❌ Wrong — xcss() called inline inside cx():
|
|
13
|
+
* xcss={cx(xcss({ color: 'red' }), xcss({ fontWeight: 'bold' }))}
|
|
14
|
+
*
|
|
15
|
+
* ❌ Also wrong — xcss() results pre-defined but still passed into cx():
|
|
16
|
+
* const baseStyles = xcss({ color: 'red' });
|
|
17
|
+
* const boldStyles = xcss({ fontWeight: 'bold' });
|
|
18
|
+
* xcss={cx(baseStyles, boldStyles)}
|
|
19
|
+
*
|
|
20
|
+
* ✅ Correct — pass xcss() results directly to the xcss prop (no cx()):
|
|
21
|
+
* const baseStyles = xcss({ color: 'red' });
|
|
22
|
+
* xcss={baseStyles}
|
|
23
|
+
*
|
|
24
|
+
* ✅ Correct — use cssMap() + cx() (cssMap is compatible with cx()):
|
|
25
|
+
* const styles = cssMap({ base: { color: 'red' } });
|
|
26
|
+
* xcss={cx(styles.base, condition && styles.focused)}
|
|
27
|
+
*
|
|
28
|
+
* This rule is import-aware: it only flags xcss() calls (inline or via variable)
|
|
29
|
+
* imported from @atlaskit/primitives inside cx() calls imported from @atlaskit/css
|
|
30
|
+
* or @compiled/react that appear inside an xcss prop.
|
|
31
|
+
*/
|
|
32
|
+
const rule = {
|
|
33
|
+
meta: {
|
|
34
|
+
type: 'problem',
|
|
35
|
+
docs: {
|
|
36
|
+
description: 'Disallow calling xcss() inline inside cx() in an xcss prop. Define styles at module level instead.'
|
|
37
|
+
},
|
|
38
|
+
messages: {
|
|
39
|
+
noXcssInCx: 'Do not pass xcss() results into cx(). ' + 'xcss() produces a StyleRule object that is incompatible with cx(), which expects Compiled atomic class names. ' + 'Pass xcss() results directly to the xcss prop instead: xcss={myStyles}. ' + 'To conditionally combine styles, use cssMap() + cx(): const styles = cssMap({...}); xcss={cx(styles.base, cond && styles.active)}'
|
|
40
|
+
},
|
|
41
|
+
schema: []
|
|
42
|
+
},
|
|
43
|
+
create(context) {
|
|
44
|
+
return {
|
|
45
|
+
JSXAttribute(node) {
|
|
46
|
+
// Narrow Rule.Node to JSXAttribute (estree-jsx augments the `estree` Node
|
|
47
|
+
// union with JSX members, so this discriminated narrowing is safe).
|
|
48
|
+
if (node.type !== 'JSXAttribute') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Only check `xcss` props
|
|
53
|
+
if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'xcss') {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!node.value || node.value.type !== 'JSXExpressionContainer') {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const expression = node.value.expression;
|
|
60
|
+
if (expression.type === 'JSXEmptyExpression') {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Early-return if the expression cannot possibly contain a cx() call —
|
|
65
|
+
// avoids the cost of getImportSources/getScope on simple references like xcss={baseStyles}.
|
|
66
|
+
const isCallOrArray = expression.type === 'CallExpression' || expression.type === 'ArrayExpression';
|
|
67
|
+
if (!isCallOrArray) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const importSources = getImportSources(context);
|
|
71
|
+
const {
|
|
72
|
+
references
|
|
73
|
+
} = getScope(context, node);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns true if an Identifier node resolves to a variable whose
|
|
77
|
+
* initializer is a call to xcss() imported from @atlaskit/primitives.
|
|
78
|
+
*
|
|
79
|
+
* Walks up the scope chain from the current JSXAttribute scope to find
|
|
80
|
+
* the variable definition, since module-level variables are not in the
|
|
81
|
+
* local scope's references list.
|
|
82
|
+
*
|
|
83
|
+
* e.g. `const baseStyles = xcss({ color: 'red' })` — passing `baseStyles`
|
|
84
|
+
* here returns true.
|
|
85
|
+
*/
|
|
86
|
+
const isXcssVariable = identNode => {
|
|
87
|
+
if (identNode.type !== 'Identifier') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const name = identNode.name;
|
|
91
|
+
// Walk up the scope chain to find the variable definition
|
|
92
|
+
let currentScope = getScope(context, node);
|
|
93
|
+
while (currentScope) {
|
|
94
|
+
const variable = currentScope.set.get(name);
|
|
95
|
+
if (variable) {
|
|
96
|
+
for (const def of variable.defs) {
|
|
97
|
+
var _def$node$init;
|
|
98
|
+
if (def.type === 'Variable' && def.node.type === 'VariableDeclarator' && ((_def$node$init = def.node.init) === null || _def$node$init === void 0 ? void 0 : _def$node$init.type) === 'CallExpression') {
|
|
99
|
+
// isXcss checks the callee identifier against referencesInScope to
|
|
100
|
+
// find the import binding. The callee lives in the same scope as the
|
|
101
|
+
// variable definition, so use all references from that scope.
|
|
102
|
+
const defScopeRefs = currentScope.references;
|
|
103
|
+
if (isXcss(def.node.init.callee, defScopeRefs, importSources)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Found the variable but it's not an xcss() call
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
currentScope = currentScope.upper;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Recursively check a node that is an argument to cx() for xcss() results —
|
|
118
|
+
* both inline calls and references to variables initialised with xcss().
|
|
119
|
+
* Recurses into LogicalExpression (&&, ||) and ConditionalExpression (? :) so that
|
|
120
|
+
* patterns like cx(cond && xcss({...})) and cx(cond ? baseStyles : a) are caught.
|
|
121
|
+
*/
|
|
122
|
+
const checkArgForXcss = argNode => {
|
|
123
|
+
if (!argNode) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Inline: cx(xcss({ color: 'red' }))
|
|
127
|
+
if (argNode.type === 'CallExpression' && isXcss(argNode.callee, references, importSources)) {
|
|
128
|
+
context.report({
|
|
129
|
+
node: argNode,
|
|
130
|
+
messageId: 'noXcssInCx'
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Variable reference: cx(baseStyles) where baseStyles = xcss({...})
|
|
135
|
+
if (isXcssVariable(argNode)) {
|
|
136
|
+
context.report({
|
|
137
|
+
node: argNode,
|
|
138
|
+
messageId: 'noXcssInCx'
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// Recurse into `cond && xcss({...})` or `cond || xcss({...})`
|
|
143
|
+
if (argNode.type === 'LogicalExpression') {
|
|
144
|
+
checkArgForXcss(argNode.left);
|
|
145
|
+
checkArgForXcss(argNode.right);
|
|
146
|
+
}
|
|
147
|
+
// Recurse into `cond ? xcss({...}) : fallback`
|
|
148
|
+
if (argNode.type === 'ConditionalExpression') {
|
|
149
|
+
checkArgForXcss(argNode.consequent);
|
|
150
|
+
checkArgForXcss(argNode.alternate);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check all arguments of a cx() call for inline xcss() calls.
|
|
156
|
+
* Reports each xcss() call found as a violation.
|
|
157
|
+
*/
|
|
158
|
+
const checkCxArgs = callNode => {
|
|
159
|
+
if (callNode.type !== 'CallExpression') {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (!isCxFunction(callNode.callee, references, importSources)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
for (const arg of callNode.arguments) {
|
|
166
|
+
if (arg) {
|
|
167
|
+
checkArgForXcss(arg.type === 'SpreadElement' ? arg.argument : arg);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Case 1: xcss={cx(...)} — cx() directly as the xcss value
|
|
173
|
+
checkCxArgs(expression);
|
|
174
|
+
|
|
175
|
+
// Case 2: xcss={[..., cx(...), ...]} — cx() inside an xcss array
|
|
176
|
+
if (expression.type === 'ArrayExpression') {
|
|
177
|
+
for (const element of expression.elements) {
|
|
178
|
+
if (element) {
|
|
179
|
+
checkCxArgs(element.type === 'SpreadElement' ? element.argument : element);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
export default rule;
|
|
@@ -91,8 +91,8 @@ function resolveVariableToConstant(programBody, variableName, cache) {
|
|
|
91
91
|
* Each argument may be a string literal or a reference to a top-level const string variable.
|
|
92
92
|
* Returns null for any argument that can't be statically resolved.
|
|
93
93
|
*/
|
|
94
|
-
function extractCallArgs(node, programBody, variableCache) {
|
|
95
|
-
if (node.arguments.length < 3) {
|
|
94
|
+
function extractCallArgs(node, programBody, variableCache, argOffset = 0) {
|
|
95
|
+
if (node.arguments.length < argOffset + 3) {
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
98
|
function resolveArg(arg) {
|
|
@@ -104,9 +104,9 @@ function extractCallArgs(node, programBody, variableCache) {
|
|
|
104
104
|
}
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
|
-
const groupId = resolveArg(node.arguments[
|
|
108
|
-
const packageId = resolveArg(node.arguments[1]);
|
|
109
|
-
const exampleId = resolveArg(node.arguments[2]);
|
|
107
|
+
const groupId = resolveArg(node.arguments[argOffset]);
|
|
108
|
+
const packageId = resolveArg(node.arguments[argOffset + 1]);
|
|
109
|
+
const exampleId = resolveArg(node.arguments[argOffset + 2]);
|
|
110
110
|
if (!groupId || !packageId || !exampleId) {
|
|
111
111
|
return null;
|
|
112
112
|
}
|
|
@@ -152,8 +152,13 @@ function resolveExamplePathFromArgs(groupId, packageId, exampleId, testFilePath)
|
|
|
152
152
|
const examplesDir = path.resolve(packagesBase.basePath, groupId, packageId, 'examples');
|
|
153
153
|
const fallback = path.resolve(examplesDir, `${exampleId}.tsx`);
|
|
154
154
|
|
|
155
|
-
//
|
|
156
|
-
|
|
155
|
+
// Phase 4: loosen candidateRe to match both pre- and post-rename filename shapes.
|
|
156
|
+
// Pre-rename: ^(\d+-)?<id>(\.(examples?))?\.tsx$
|
|
157
|
+
// Post-rename: ^(\d+-)?<id>(\.<ident>){0,3}\.tsx$ (Volt prefix, optional .dup<N>, optional role)
|
|
158
|
+
// The {0,3} cap prevents matching arbitrary strings (e.g. 4-component names).
|
|
159
|
+
// Escape regex metacharacters in exampleId (ids are kebab-case today, but defensive).
|
|
160
|
+
const escapedId = exampleId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
161
|
+
const candidateRe = new RegExp(`^(?:\\d+-)?${escapedId}(?:\\.[A-Za-z][A-Za-z0-9_]*){0,3}\\.tsx$`);
|
|
157
162
|
try {
|
|
158
163
|
const match = fs.readdirSync(examplesDir).find(f => candidateRe.test(f));
|
|
159
164
|
if (match) {
|
|
@@ -248,20 +253,24 @@ const rule = {
|
|
|
248
253
|
return;
|
|
249
254
|
}
|
|
250
255
|
const node = estreeNode;
|
|
251
|
-
|
|
252
|
-
|
|
256
|
+
let calleeIdentifier = null;
|
|
257
|
+
let argOffset = 0;
|
|
258
|
+
if (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'visitExample') {
|
|
259
|
+
calleeIdentifier = node.callee.property;
|
|
260
|
+
} else if (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'visitMockedExample') {
|
|
261
|
+
calleeIdentifier = node.callee;
|
|
262
|
+
argOffset = 1; // first arg is `page`
|
|
263
|
+
} else {
|
|
253
264
|
return;
|
|
254
265
|
}
|
|
255
266
|
|
|
256
|
-
// Narrow callee — we've confirmed property is an Identifier above
|
|
257
|
-
const callee = node.callee;
|
|
258
267
|
// reportCallee is typed as estree.Node for context.report compatibility
|
|
259
268
|
const reportCallee = estreeNode.callee;
|
|
260
269
|
const genericType = extractGenericType(node);
|
|
261
270
|
|
|
262
271
|
// ── Case 1: No generic type parameter ────────────────────────────────
|
|
263
272
|
if (genericType === null) {
|
|
264
|
-
const args = extractCallArgs(node, programBody, variableCache);
|
|
273
|
+
const args = extractCallArgs(node, programBody, variableCache, argOffset);
|
|
265
274
|
context.report({
|
|
266
275
|
node: reportCallee,
|
|
267
276
|
messageId: 'missingTypeofImport',
|
|
@@ -274,7 +283,7 @@ const rule = {
|
|
|
274
283
|
return null;
|
|
275
284
|
}
|
|
276
285
|
const importPath = computeRelativeImportPath(filename, examplePath);
|
|
277
|
-
const [start, end] =
|
|
286
|
+
const [start, end] = calleeIdentifier.range;
|
|
278
287
|
return fixer.insertTextAfterRange([start, end], `<typeof import('${importPath}')>`);
|
|
279
288
|
}
|
|
280
289
|
});
|
|
@@ -333,7 +342,7 @@ const rule = {
|
|
|
333
342
|
}
|
|
334
343
|
|
|
335
344
|
// Validate that the import path matches the arguments
|
|
336
|
-
const args = extractCallArgs(node, programBody, variableCache);
|
|
345
|
+
const args = extractCallArgs(node, programBody, variableCache, argOffset);
|
|
337
346
|
if (!args) {
|
|
338
347
|
// Dynamic arguments — can't validate statically
|
|
339
348
|
return;
|
package/dist/esm/index.js
CHANGED
|
@@ -28,6 +28,9 @@ import useRecommendedUtils from './rules/feature-gating/use-recommended-utils';
|
|
|
28
28
|
import validGateName from './rules/feature-gating/valid-gate-name';
|
|
29
29
|
import expandBackgroundShorthand from './rules/compiled/expand-background-shorthand';
|
|
30
30
|
import expandSpacingShorthand from './rules/compiled/expand-spacing-shorthand';
|
|
31
|
+
import noCssPropInObjectSpread from './rules/compiled/no-css-prop-in-object-spread';
|
|
32
|
+
import useMotionTokenValues from './rules/compiled/use-motion-token-values';
|
|
33
|
+
import expandMotionShorthand from './rules/compiled/expand-motion-shorthand';
|
|
31
34
|
import noSparseCheckout from './rules/no-sparse-checkout';
|
|
32
35
|
import noDirectDocumentUsage from './rules/no-direct-document-usage';
|
|
33
36
|
import noSetImmediate from './rules/no-set-immediate';
|
|
@@ -39,7 +42,9 @@ import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
|
|
|
39
42
|
import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-imports';
|
|
40
43
|
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
41
44
|
import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
|
|
45
|
+
import editorExampleTypeImportRequired from './rules/editor-example-type-import-required';
|
|
42
46
|
import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
|
|
47
|
+
import noXcssInCx from './rules/no-xcss-in-cx';
|
|
43
48
|
import { join, normalize } from 'node:path';
|
|
44
49
|
import { readFileSync } from 'node:fs';
|
|
45
50
|
var jiraRoot;
|
|
@@ -70,6 +75,7 @@ var rules = {
|
|
|
70
75
|
'expand-border-shorthand': expandBorderShorthand,
|
|
71
76
|
'expand-background-shorthand': expandBackgroundShorthand,
|
|
72
77
|
'expand-spacing-shorthand': expandSpacingShorthand,
|
|
78
|
+
'no-css-prop-in-object-spread': noCssPropInObjectSpread,
|
|
73
79
|
'no-duplicate-dependencies': noDuplicateDependencies,
|
|
74
80
|
'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage,
|
|
75
81
|
'no-pre-post-install-scripts': noPreAndPostInstallScripts,
|
|
@@ -96,7 +102,11 @@ var rules = {
|
|
|
96
102
|
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
97
103
|
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
|
|
98
104
|
'visit-example-type-import-required': visitExampleTypeImportRequired,
|
|
99
|
-
'
|
|
105
|
+
'no-xcss-in-cx': noXcssInCx,
|
|
106
|
+
'editor-example-type-import-required': editorExampleTypeImportRequired,
|
|
107
|
+
'ensure-use-sync-external-store-server-snapshot': ensureUseSyncExternalStoreServerSnapshot,
|
|
108
|
+
'use-motion-token-values': useMotionTokenValues,
|
|
109
|
+
'expand-motion-shorthand': expandMotionShorthand
|
|
100
110
|
};
|
|
101
111
|
var commonConfig = {
|
|
102
112
|
'@atlaskit/platform/ensure-test-runner-arguments': 'error',
|
|
@@ -108,10 +118,14 @@ var commonConfig = {
|
|
|
108
118
|
'@atlaskit/platform/no-module-level-eval-nav4': 'error',
|
|
109
119
|
'@atlaskit/platform/no-direct-document-usage': 'warn',
|
|
110
120
|
'@atlaskit/platform/no-set-immediate': 'error',
|
|
121
|
+
'@atlaskit/platform/no-xcss-in-cx': 'error',
|
|
111
122
|
// Compiled: rules that are not included via `@compiled/recommended
|
|
112
123
|
'@atlaskit/platform/expand-border-shorthand': 'error',
|
|
113
124
|
'@atlaskit/platform/expand-background-shorthand': 'error',
|
|
114
125
|
'@atlaskit/platform/expand-spacing-shorthand': 'error',
|
|
126
|
+
'@atlaskit/platform/no-css-prop-in-object-spread': 'error',
|
|
127
|
+
'@atlaskit/platform/use-motion-token-values': 'warn',
|
|
128
|
+
'@atlaskit/platform/expand-motion-shorthand': 'warn',
|
|
115
129
|
'@compiled/jsx-pragma': ['error', {
|
|
116
130
|
importSources: ['@atlaskit/css'],
|
|
117
131
|
onlyRunIfImportingCompiled: true,
|
|
@@ -153,7 +167,6 @@ var plugin = {
|
|
|
153
167
|
get '@atlaskit/platform'() {
|
|
154
168
|
return plugin;
|
|
155
169
|
},
|
|
156
|
-
// @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
|
|
157
170
|
'@compiled': {
|
|
158
171
|
meta: compiledPlugin.meta,
|
|
159
172
|
rules: compiledPlugin.rules
|
|
@@ -170,7 +183,6 @@ var plugin = {
|
|
|
170
183
|
get '@atlaskit/platform'() {
|
|
171
184
|
return plugin;
|
|
172
185
|
},
|
|
173
|
-
// @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
|
|
174
186
|
'@compiled': {
|
|
175
187
|
meta: compiledPlugin.meta,
|
|
176
188
|
rules: compiledPlugin.rules
|