diff --git a/apps/docs/docs/api/configuration/eslint-plugin.mdx b/apps/docs/docs/api/configuration/eslint-plugin.mdx index d60de143..04b256df 100644 --- a/apps/docs/docs/api/configuration/eslint-plugin.mdx +++ b/apps/docs/docs/api/configuration/eslint-plugin.mdx @@ -17,7 +17,7 @@ type Options = { // Possible strings where you can import stylex from // // Default: ['@stylexjs/stylex'] - validImports: Array, + validImports: Array, // Custom limits for values of various properties propLimits?: PropLimits, @@ -99,7 +99,7 @@ type Options = { // Possible string where you can import stylex from // // Default: ['@stylexjs/stylex'] - validImports: Array, + validImports: Array, // Minimum number of keys required after which the rule is enforced // diff --git a/packages/eslint-plugin/__tests__/stylex-sort-keys-test.js b/packages/eslint-plugin/__tests__/stylex-sort-keys-test.js index 953e0aa0..2256400d 100644 --- a/packages/eslint-plugin/__tests__/stylex-sort-keys-test.js +++ b/packages/eslint-plugin/__tests__/stylex-sort-keys-test.js @@ -117,6 +117,18 @@ eslintTester.run('stylex-sort-keys', rule.default, { }); `, }, + { + options: [{ validImports: [{ from: 'a', as: 'css' }] }], + code: ` + import { css } from 'a'; + const styles = css.create({ + button: { + borderColor: 'black', + display: 'flex', + } + }); + `, + }, { code: ` import { keyframes } from 'stylex'; @@ -672,5 +684,34 @@ eslintTester.run('stylex-sort-keys', rule.default, { }, ], }, + { + options: [{ validImports: [{ from: 'a', as: 'css' }] }], + code: ` + import { css } from 'a'; + const styles = css.create({ + main: { + padding: 10, + animationDuration: '100ms', + fontSize: 12, + } + }); + `, + output: ` + import { css } from 'a'; + const styles = css.create({ + main: { + animationDuration: '100ms', + padding: 10, + fontSize: 12, + } + }); + `, + errors: [ + { + message: + 'StyleX property key "animationDuration" should be above "padding"', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js b/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js index dec39717..518592d0 100644 --- a/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js +++ b/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js @@ -1387,7 +1387,23 @@ This property is not supported in legacy StyleX resolution.`, }, { code: ` - import stylex from'stylex'; + import { css } from 'a'; + const styles = css.create({ + base:{ + background: '' + }, + }); + `, + options: [{ validImports: [{ from: 'a', as: 'css' }] }], + errors: [ + { + message: 'The empty string is not allowed by Stylex.', + }, + ], + }, + { + code: ` + import stylex from'stylex'; const styles = stylex.create({ b:{ textUnderlineOffset: '', @@ -1406,8 +1422,6 @@ initial inherit unset revert`, - }, - ], }, ], }); diff --git a/packages/eslint-plugin/src/stylex-sort-keys.js b/packages/eslint-plugin/src/stylex-sort-keys.js index 5cfe72eb..19a44556 100644 --- a/packages/eslint-plugin/src/stylex-sort-keys.js +++ b/packages/eslint-plugin/src/stylex-sort-keys.js @@ -25,7 +25,13 @@ import getPropertyPriorityAndType from './utils/getPropertyPriorityAndType'; /*:: import { Rule } from 'eslint'; */ type Schema = { - validImports: Array, + validImports: Array< + | string + | { + from: string, + as: string, + }, + >, minKeys: number, allowLineSeparatedGroups: boolean, }; @@ -64,7 +70,18 @@ const stylexSortKeys = { properties: { validImports: { type: 'array', - items: { type: 'string' }, + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + from: { type: 'string' }, + as: { type: 'string' }, + }, + }, + ], + }, default: ['stylex', '@stylexjs/stylex'], }, minKeys: { @@ -128,32 +145,47 @@ const stylexSortKeys = { return; } - if (!importsToLookFor.includes(node.source.value)) { - return; - } - - node.specifiers.forEach((specifier) => { - if ( - specifier.type === 'ImportDefaultSpecifier' || - specifier.type === 'ImportNamespaceSpecifier' - ) { - styleXDefaultImports.add(specifier.local.name); + const foundImportSource = importsToLookFor.find((importSource) => { + if (typeof importSource === 'string') { + return importSource === node.source.value; } + return importSource.from === node.source.value; + }); - if ( - specifier.type === 'ImportSpecifier' && - specifier.imported.name === 'create' - ) { - styleXCreateImports.add(specifier.local.name); - } + if (typeof foundImportSource === 'string') { + node.specifiers.forEach((specifier) => { + if ( + specifier.type === 'ImportDefaultSpecifier' || + specifier.type === 'ImportNamespaceSpecifier' + ) { + styleXDefaultImports.add(specifier.local.name); + } - if ( - specifier.type === 'ImportSpecifier' && - specifier.imported.name === 'keyframes' - ) { - styleXKeyframesImports.add(specifier.local.name); - } - }); + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.name === 'create' + ) { + styleXCreateImports.add(specifier.local.name); + } + + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.name === 'keyframes' + ) { + styleXKeyframesImports.add(specifier.local.name); + } + }); + } + + if (typeof foundImportSource === 'object') { + node.specifiers.forEach((specifier) => { + if (specifier.type === 'ImportSpecifier') { + if (specifier.imported.name === foundImportSource.as) { + styleXDefaultImports.add(specifier.local.name); + } + } + }); + } }, CallExpression( node: $ReadOnly<{ ...CallExpression, ...Rule.NodeParentExtension }>, diff --git a/packages/eslint-plugin/src/stylex-valid-styles.js b/packages/eslint-plugin/src/stylex-valid-styles.js index 70cc7761..12156c2a 100644 --- a/packages/eslint-plugin/src/stylex-valid-styles.js +++ b/packages/eslint-plugin/src/stylex-valid-styles.js @@ -2280,7 +2280,18 @@ const stylexValidStyles = { properties: { validImports: { type: 'array', - items: { type: 'string' }, + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + from: { type: 'string' }, + as: { type: 'string' }, + }, + }, + ], + }, default: ['stylex', '@stylexjs/stylex'], }, allowOuterPseudoAndMedia: { @@ -2336,7 +2347,13 @@ const stylexValidStyles = { }; type Schema = { - validImports: Array, + validImports: Array< + | string + | { + from: string, + as: string, + }, + >, allowOuterPseudoAndMedia: boolean, banPropsForLegacy: boolean, propLimits?: PropLimits, @@ -2801,7 +2818,15 @@ const stylexValidStyles = { return; } const sourceValue = node.source.value; - const isStylexImport = importsToLookFor.includes(sourceValue); + + const foundImportSource = importsToLookFor.find((importSource) => { + if (typeof importSource === 'string') { + return importSource === sourceValue; + } + return importSource.from === sourceValue; + }); + + const isStylexImport = foundImportSource !== undefined; const isStylexDefineVarsImport = sourceValue.endsWith( stylexDefineVarsFileExtension, ); @@ -2809,26 +2834,38 @@ const stylexValidStyles = { return; } if (isStylexImport) { - node.specifiers.forEach((specifier) => { - if ( - specifier.type === 'ImportDefaultSpecifier' || - specifier.type === 'ImportNamespaceSpecifier' - ) { - styleXDefaultImports.add(specifier.local.name); - } - if ( - specifier.type === 'ImportSpecifier' && - specifier.imported.name === 'create' - ) { - styleXCreateImports.add(specifier.local.name); - } - if ( - specifier.type === 'ImportSpecifier' && - specifier.imported.name === 'keyframes' - ) { - styleXKeyframesImports.add(specifier.local.name); - } - }); + if (typeof foundImportSource === 'string') { + node.specifiers.forEach((specifier) => { + if ( + specifier.type === 'ImportDefaultSpecifier' || + specifier.type === 'ImportNamespaceSpecifier' + ) { + styleXDefaultImports.add(specifier.local.name); + } + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.name === 'create' + ) { + styleXCreateImports.add(specifier.local.name); + } + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.name === 'keyframes' + ) { + styleXKeyframesImports.add(specifier.local.name); + } + }); + } + + if (typeof foundImportSource === 'object') { + node.specifiers.forEach((specifier) => { + if (specifier.type === 'ImportSpecifier') { + if (specifier.imported.name === foundImportSource.as) { + styleXDefaultImports.add(specifier.local.name); + } + } + }); + } } if (isStylexDefineVarsImport) {