Last active
December 30, 2021 01:48
-
-
Save fersilva16/d585a4a032e9724639e62ef2a9ec4fcd to your computer and use it in GitHub Desktop.
Transform imports to namespace import
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { load } from '@workspace/shared'; | |
import Foo from '@workspace/shared'; | |
import * as Bar from '@workspace/shared'; | |
load(); | |
Foo.doSomething(); | |
Bar.default.doSomething(); | |
function test() { | |
const load = {}; | |
const Foo = {}; | |
Bar.load(); | |
Bar.default.doSomething(); | |
console.log(load); | |
console.log(Foo); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as Test from '@workspace/shared'; | |
Test.load(); | |
Test.default.doSomething(); | |
Test.default.doSomething(); | |
function test() { | |
const load = {}; | |
const Foo = {}; | |
Test.load(); | |
Test.default.doSomething(); | |
console.log(load); | |
console.log(Foo); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { | |
API, | |
FileInfo, | |
ASTNode, | |
BlockStatement, | |
ASTPath, | |
Identifier, | |
} from 'jscodeshift'; | |
/** | |
* Import source: package or the path to a file. | |
* Examples: | |
* - fs | |
* - ./filename | |
* - ../path/to/file.ts | |
*/ | |
// const importSource = './path/to/file'; | |
const importSource = '@workspace/shared'; | |
/** | |
* Name of the namespace. | |
*/ | |
const namespace = 'Test'; | |
type ASTPathx<T> = Omit<ASTPath<T>, 'parent'> & { parent: ASTPath<ASTNode> }; | |
export default function transformToNamespaceImport( | |
fileInfo: FileInfo, | |
{ jscodeshift }: API, | |
) { | |
const root = jscodeshift(fileInfo.source); | |
const imports = new Map<string, ASTNode>(); | |
const scopesWithDeclaration = new Map<BlockStatement, Set<string>>(); | |
function getBlockStatement({ | |
node, | |
parent, | |
}: ASTPathx<ASTNode>): BlockStatement | undefined { | |
if (node.type === 'Program') return undefined; | |
if (node.type === 'BlockStatement') return node; | |
return getBlockStatement(parent); | |
} | |
const importDeclarations = root.find(jscodeshift.ImportDeclaration, { | |
source: { | |
value: importSource, | |
}, | |
}); | |
if (!importDeclarations.length) { | |
console.log(`Import declaration with source "${importSource}" not found!`); | |
return; | |
} | |
importDeclarations | |
.forEach(({ node }) => { | |
node.specifiers!.forEach((specifier) => { | |
switch (specifier.type) { | |
case 'ImportSpecifier': | |
imports.set( | |
specifier.local!.name || specifier.imported.name, | |
jscodeshift.memberExpression( | |
jscodeshift.identifier(namespace), | |
jscodeshift.identifier(specifier.imported.name), | |
), | |
); | |
break; | |
case 'ImportDefaultSpecifier': | |
imports.set( | |
specifier.local!.name, | |
jscodeshift.memberExpression( | |
jscodeshift.identifier(namespace), | |
jscodeshift.identifier('default'), | |
), | |
); | |
break; | |
case 'ImportNamespaceSpecifier': | |
imports.set( | |
specifier.local!.name, | |
jscodeshift.identifier(namespace), | |
); | |
break; | |
} | |
}); | |
}) | |
.replaceWith((_, index) => { | |
if (index === 0) { | |
return jscodeshift.importDeclaration( | |
[ | |
jscodeshift.importNamespaceSpecifier( | |
jscodeshift.identifier(namespace), | |
), | |
], | |
jscodeshift.stringLiteral(importSource), | |
); | |
} | |
return undefined; | |
}); | |
root | |
.find(jscodeshift.Identifier) | |
.filter(({ node, parent }: ASTPathx<Identifier>) => { | |
if ( | |
[ | |
'ImportSpecifier', | |
'ImportDefaultSpecifier', | |
'ImportNamespaceSpecifier', | |
].includes(parent.node.type) | |
) { | |
return false; | |
} | |
if (parent.node.type === 'MemberExpression') { | |
return parent.node.object === node; | |
} | |
if (!imports.has(node.name)) return false; | |
if ( | |
['VariableDeclarator', 'FunctionDeclaration'].includes(parent.node.type) | |
) { | |
const blockStatement = getBlockStatement(parent); | |
if (blockStatement) { | |
const ignoreds = scopesWithDeclaration.get(blockStatement); | |
if (!ignoreds) { | |
scopesWithDeclaration.set(blockStatement, new Set([node.name])); | |
} else { | |
ignoreds.add(node.name); | |
} | |
} | |
return false; | |
} | |
return true; | |
}) | |
.filter(({ node, parent }) => { | |
if (!imports.has(node.name)) return false; | |
const blockStatement = getBlockStatement(parent); | |
if (blockStatement) { | |
const variables = scopesWithDeclaration.get(blockStatement); | |
if (!variables) return false; | |
return !variables.has(node.name); | |
} | |
return true; | |
}) | |
.replaceWith(({ node }) => imports.get(node.name)); | |
return root.toSource(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment