From 64e3cbd8aa16b2e1159c594716d196bf523544b8 Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Fri, 12 Sep 2025 17:05:24 +0000 Subject: [PATCH 1/2] Fix for deprecated and experimental import paths --- .../__tests__/use-styled-react-import.test.js | 20 +++++++ src/rules/use-styled-react-import.js | 57 ++++++++++--------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/rules/__tests__/use-styled-react-import.test.js b/src/rules/__tests__/use-styled-react-import.test.js index 5327552..db4489a 100644 --- a/src/rules/__tests__/use-styled-react-import.test.js +++ b/src/rules/__tests__/use-styled-react-import.test.js @@ -56,6 +56,26 @@ ruleTester.run('use-styled-react-import', rule, { ], }, + // Invalid: Imports from /experimental and /deprecated paths + { + code: `import { Button } from '@primer/react/experimental' + import { Box } from '@primer/react/deprecated' + const Component = () => `, + output: `import { Button } from '@primer/styled-react/experimental' + import { Box } from '@primer/styled-react/deprecated' + const Component = () => `, + errors: [ + { + messageId: 'useStyledReactImport', + data: {componentName: 'Button'}, + }, + { + messageId: 'useStyledReactImport', + data: {componentName: 'Box'}, + }, + ], + }, + // Invalid: ActionList.Item with sx prop and ActionList imported from @primer/react { code: `import { ActionList } from '@primer/react' diff --git a/src/rules/use-styled-react-import.js b/src/rules/use-styled-react-import.js index 6831903..9b1d8ff 100644 --- a/src/rules/use-styled-react-import.js +++ b/src/rules/use-styled-react-import.js @@ -88,8 +88,8 @@ module.exports = { ImportDeclaration(node) { const importSource = node.source.value - if (importSource === '@primer/react') { - // Track imports from @primer/react + if (importSource === '@primer/react' || importSource.startsWith('@primer/react/')) { + // Track imports from @primer/react and its subpaths for (const specifier of node.specifiers) { if (specifier.type === 'ImportSpecifier') { const importedName = specifier.imported.name @@ -98,16 +98,16 @@ module.exports = { styledTypes.has(importedName) || styledUtilities.has(importedName) ) { - primerReactImports.set(importedName, {node, specifier}) + primerReactImports.set(importedName, {node, specifier, importSource}) } } } - } else if (importSource === '@primer/styled-react') { - // Track what's imported from styled-react + } else if (importSource === '@primer/styled-react' || importSource.startsWith('@primer/styled-react/')) { + // Track what's imported from styled-react and its subpaths for (const specifier of node.specifiers) { if (specifier.type === 'ImportSpecifier') { const importedName = specifier.imported.name - styledReactImports.set(importedName, {node, specifier}) + styledReactImports.set(importedName, {node, specifier, importSource}) } } } @@ -167,7 +167,7 @@ module.exports = { messageId: 'useStyledReactImport', data: {componentName}, fix(fixer) { - const {node: importNode} = importInfo + const {node: importNode, importSource} = importInfo const changes = importNodeChanges.get(importNode) if (!changes) { @@ -190,21 +190,24 @@ module.exports = { return !componentsToMove.has(name) }) + // Convert @primer/react path to @primer/styled-react path + const styledReactPath = importSource.replace('@primer/react', '@primer/styled-react') + // If no components remain, replace with new import directly if (remainingSpecifiers.length === 0) { const movedComponents = changes.toMove.join(', ') - fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '@primer/styled-react'`)) + fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${styledReactPath}'`)) } else { // Otherwise, update the import to only include remaining components const remainingNames = remainingSpecifiers.map(spec => spec.imported.name) fixes.push( - fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '@primer/react'`), + fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`), ) // Add new styled-react import const movedComponents = changes.toMove.join(', ') fixes.push( - fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '@primer/styled-react'`), + fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${styledReactPath}'`), ) } @@ -250,7 +253,7 @@ module.exports = { messageId: 'usePrimerReactImport', data: {componentName}, fix(fixer) { - const {node: importNode} = importInfo + const {node: importNode, importSource} = importInfo const changes = styledReactImportNodeChanges.get(importNode) if (!changes) { @@ -273,6 +276,9 @@ module.exports = { return !componentsToMove.has(name) }) + // Convert @primer/styled-react path to @primer/react path + const primerReactPath = importSource.replace('@primer/styled-react', '@primer/react') + // Check if there's an existing primer-react import to merge with const existingPrimerReactImport = Array.from(primerReactImportNodes)[0] @@ -286,7 +292,7 @@ module.exports = { fixes.push( fixer.replaceText( existingPrimerReactImport, - `import { ${newSpecifiers.join(', ')} } from '@primer/react'`, + `import { ${newSpecifiers.join(', ')} } from '${primerReactPath}'`, ), ) fixes.push(fixer.remove(importNode)) @@ -300,33 +306,29 @@ module.exports = { fixes.push( fixer.replaceText( existingPrimerReactImport, - `import { ${newSpecifiers.join(', ')} } from '@primer/react'`, + `import { ${newSpecifiers.join(', ')} } from '${primerReactPath}'`, ), ) const remainingNames = remainingSpecifiers.map(spec => spec.imported.name) fixes.push( - fixer.replaceText( - importNode, - `import { ${remainingNames.join(', ')} } from '@primer/styled-react'`, - ), + fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`), ) } else if (remainingSpecifiers.length === 0) { // Case: No existing primer-react import, no remaining styled-react imports const movedComponents = changes.toMove.join(', ') - fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '@primer/react'`)) + fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${primerReactPath}'`)) } else { // Case: No existing primer-react import, some styled-react imports remain const remainingNames = remainingSpecifiers.map(spec => spec.imported.name) fixes.push( - fixer.replaceText( - importNode, - `import { ${remainingNames.join(', ')} } from '@primer/styled-react'`, - ), + fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`), ) const movedComponents = changes.toMove.join(', ') - fixes.push(fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '@primer/react'`)) + fixes.push( + fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${primerReactPath}'`), + ) } return fixes @@ -343,13 +345,16 @@ module.exports = { messageId: 'moveToStyledReact', data: {importName}, fix(fixer) { - const {node: importNode, specifier} = importInfo + const {node: importNode, specifier, importSource} = importInfo const otherSpecifiers = importNode.specifiers.filter(s => s !== specifier) + // Convert @primer/react path to @primer/styled-react path + const styledReactPath = importSource.replace('@primer/react', '@primer/styled-react') + // If this is the only import, replace the whole import if (otherSpecifiers.length === 0) { const prefix = styledTypes.has(importName) ? 'type ' : '' - return fixer.replaceText(importNode, `import { ${prefix}${importName} } from '@primer/styled-react'`) + return fixer.replaceText(importNode, `import { ${prefix}${importName} } from '${styledReactPath}'`) } // Otherwise, remove from current import and add new import @@ -377,7 +382,7 @@ module.exports = { // Add new import const prefix = styledTypes.has(importName) ? 'type ' : '' fixes.push( - fixer.insertTextAfter(importNode, `\nimport { ${prefix}${importName} } from '@primer/styled-react'`), + fixer.insertTextAfter(importNode, `\nimport { ${prefix}${importName} } from '${styledReactPath}'`), ) return fixes From 0159ee37d33a2e87531546280cadcf03a3e6410e Mon Sep 17 00:00:00 2001 From: Jon Rohan Date: Fri, 12 Sep 2025 10:26:09 -0700 Subject: [PATCH 2/2] Update eslint-plugin-primer-react import paths Fixes deprecated and experimental import paths in eslint-plugin-primer-react. --- .changeset/cyan-carrots-boil.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cyan-carrots-boil.md diff --git a/.changeset/cyan-carrots-boil.md b/.changeset/cyan-carrots-boil.md new file mode 100644 index 0000000..60b0eea --- /dev/null +++ b/.changeset/cyan-carrots-boil.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-primer-react": patch +--- + +Fix for deprecated and experimental import paths