@babel/traverse#NodePath TypeScript Examples
The following examples show how to use
@babel/traverse#NodePath.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From vidact with MIT License | 7 votes |
export function getNodePathForType<
Type extends t.Node["type"],
NodeType = Extract<t.Node, { type: Type }>,
NodePathType = NodePath<NodeType>
>(node: t.Node, type: Type) {
const paths: NodePathType[] = [];
traverse(node, {
[type]: (path: NodePathType) => {
paths.push(path);
}
});
return paths;
}
Example #2
Source File: babelInlineConverters.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 6 votes |
private slicedToArrayFunction(path: NodePath<t.VariableDeclarator>) {
if (path.removed || !t.isCallExpression(path.node.init) || path.node.init.arguments.length !== 0) return;
let hasErrorString = false;
path.traverse({
StringLiteral: (stringPath) => {
if (stringPath.node.value === 'Invalid attempt to destructure non-iterable instance') {
hasErrorString = true;
}
},
});
if (!hasErrorString) return;
this.debugLog('replace var incline babel slicedToArray function:');
this.debugLog(this.debugPathToCode(path));
path.get('init').replaceWith(t.callExpression(t.identifier('require'), [t.stringLiteral('@babel/runtime/helpers/slicedToArray')]));
}
Example #3
Source File: resolveJsxComponent.ts From react-optimized-image with MIT License | 6 votes |
resolveObjectProperty = (path: NodePath<ObjectExpression>, property: ObjectProperty): string[] => {
let bindings: string[] = [];
const parent = path.findParent(() => true);
if (parent.node.type === 'ObjectProperty') {
bindings = [...resolveObjectProperty(parent.findParent(() => true) as NodePath<ObjectExpression>, parent.node)];
} else if (parent.node.type === 'VariableDeclarator' && parent.node.id.type === 'Identifier') {
bindings.push(parent.node.id.name);
}
bindings.push(property.key.name);
return bindings;
}
Example #4
Source File: webpackParser.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 6 votes |
private parseArray(file: File, ast: NodePath<ArrayExpression>, modules: Module[]): void {
ast.get('elements').forEach((element, i) => {
if (!element.isFunctionExpression()) return;
if (element.node.body.body.length === 0) return;
const dependencyValues: number[] = [];
const requireIdentifer = element.node.params[2];
if (isIdentifier(requireIdentifer)) {
element.traverse({
CallExpression: (dependencyPath) => {
if (!isIdentifier(dependencyPath.node.callee) || !isNumericLiteral(dependencyPath.node.arguments[0])) return;
if (dependencyPath.scope.bindingIdentifierEquals(dependencyPath.node.callee.name, requireIdentifer)) {
dependencyValues[dependencyPath.node.arguments[0].value] = dependencyPath.node.arguments[0].value;
}
},
});
}
const newModule = new Module(file, element, i, dependencyValues, this.PARAM_MAPPING);
newModule.calculateFields();
modules[i] = newModule;
});
}
Example #5
Source File: resolveJsxComponent.ts From react-optimized-image with MIT License | 6 votes |
resolveJsxComponent = (types: Babel['types'], path: NodePath<JSXElement>): string | undefined => {
// check if it is a possible react-optimized-image component before proceeding further
const srcAttribute = getAttribute(path, 'src');
if (!srcAttribute) {
return;
}
const requireName = getRelevantRequireString(types, srcAttribute);
// check if the imported src is not an image in case an extension is present
if ((!requireName || !requireName.match(/\.(jpe?g|png|svg|gif|webp)($|\?)/gi)) && requireName !== '') {
return;
}
// it is now likely to be a react-optimized-image component, so start resolving
// check for a normal opening element (<Img ...)
if (path.node.openingElement.name.type === 'JSXIdentifier') {
const binding = path.scope.getBinding(path.node.openingElement.name.name);
const component = getImportedJsxComponent(binding);
return component;
}
// check for an object opening element (<styles.Img ...)
if (path.node.openingElement.name.type === 'JSXMemberExpression') {
const objectBindings = resolveJsxMemberExpression(path.node.openingElement.name);
const resolvedBinding = resolveObject(types, path, objectBindings);
const component = getImportedJsxComponent(resolvedBinding);
return component;
}
}
Example #6
Source File: component.ts From reskript with MIT License | 6 votes |
resolveCalleeName = (path: NodePath<babel.types.CallExpression>) => {
const callee = path.get('callee');
if (callee.isIdentifier()) {
return callee.node.name;
}
if (callee.isMemberExpression()) {
const property = callee.get('property');
return property.isIdentifier() ? property.node.name : '';
}
return '';
}
Example #7
Source File: get-import-nodes.ts From prettier-plugin-sort-imports with Apache License 2.0 | 6 votes |
getImportNodes = (code: string, options?: ParserOptions) => {
const importNodes: ImportDeclaration[] = [];
const ast = babelParser(code, {
...options,
sourceType: 'module',
});
traverse(ast, {
ImportDeclaration(path: NodePath<ImportDeclaration>) {
const tsModuleParent = path.findParent((p) =>
isTSModuleDeclaration(p),
);
if (!tsModuleParent) {
importNodes.push(path.node);
}
},
});
return importNodes;
}
Example #8
Source File: component.ts From reskript with MIT License | 6 votes |
resolveComponentName = (declaration: NodePath<FunctionDeclaration>, filename: string | undefined) => {
const functionName = declaration.node.id?.name;
if (functionName) {
return functionName;
}
if (!filename) {
return 'Unknown';
}
const file = path.basename(filename, path.extname(filename));
return file === 'index' ? path.dirname(filename).split(path.sep).pop() : file;
}
Example #9
Source File: plugin.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 6 votes |
protected getModuleDependency(path: NodePath<t.CallExpression>): Module | null {
if (!t.isIdentifier(path.node.callee)) return null;
if (!t.isNumericLiteral(path.node.arguments[0]) && !t.isMemberExpression(path.node.arguments[0]) && !t.isStringLiteral(path.node.arguments[0])) return null;
if (path.scope.getBindingIdentifier(path.node.callee.name)?.start !== this.module.requireParam?.start) return null;
if (t.isMemberExpression(path.node.arguments[0]) && t.isNumericLiteral(path.node.arguments[0].property)) {
return this.moduleList[this.module.dependencies[path.node.arguments[0].property.value]] ?? null;
}
if (t.isStringLiteral(path.node.arguments[0])) {
const nonNpmRegexTest = /\.\/([0-9]+)/.exec(path.node.arguments[0].value);
if (nonNpmRegexTest != null) {
return this.moduleList[this.module.dependencies[+nonNpmRegexTest[1]]];
}
return this.moduleList.find((mod) => t.isStringLiteral(path.node.arguments[0]) && mod?.moduleName === path.node.arguments[0].value) ?? null;
}
if (t.isNumericLiteral(path.node.arguments[0])) {
return this.moduleList[this.module.dependencies[path.node.arguments[0].value]] ?? null;
}
return null;
}
Example #10
Source File: utils.ts From vidact with MIT License | 6 votes |
export function createNodePath<T extends t.Node>(node: T) {
const path: NodePath<T> = new NodePath(new Hub(null, {}), null);
path.node = node;
return path;
}
Example #11
Source File: plugin.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 6 votes |
protected variableIsForDependency(path: NodePath<t.VariableDeclarator> | NodePath<t.ImportDeclaration>, dep: string | string[]): boolean {
const depArray = dep instanceof Array ? dep : [dep];
if (path.isVariableDeclarator()) {
const callExpression = path.get('init');
if (!callExpression.isCallExpression()) return false;
const requireValue = t.isStringLiteral(callExpression.node.arguments[0]) ? callExpression.node.arguments[0].value : null;
const dependencyName = this.getModuleDependency(callExpression)?.moduleName ?? requireValue ?? '';
return depArray.includes(dependencyName);
}
if (path.isImportDeclaration()) {
if (!t.isStringLiteral(path.node.source)) return false;
return depArray.includes(path.node.source.value);
}
return false;
}
Example #12
Source File: createStatementUpdater.ts From vidact with MIT License | 6 votes |
export function createStatementUpdater(
statement: NodePath | t.Statement,
scope: Scope | t.Identifier,
name = STATEMENT_EXECUTER_VAR
): [t.VariableDeclaration, t.ExpressionStatement, t.Identifier] {
const uid = t.isIdentifier(scope) ? scope : scope.generateUidIdentifier(name);
const node = "node" in statement ? statement.node : statement;
const block = t.isBlockStatement(node)
? node
: t.blockStatement([node as t.Statement]);
const nodeToReplace = t.variableDeclaration("const", [
t.variableDeclarator(uid, t.arrowFunctionExpression([], block))
]);
const callExpression = t.expressionStatement(t.callExpression(uid, []));
return [nodeToReplace, callExpression, uid];
}
Example #13
Source File: plugin.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 6 votes |
/**
* [DEBUG] Returns the code of the path
* @param path The path to generate code from
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected debugPathToCode(path: NodePath<any>): string {
if (!debug(this.getDebugName()).enabled) return '';
return generator({
...this.module.originalFile.program,
type: 'Program',
body: [t.isStatement(path.node) ? path.node : t.expressionStatement(path.node)],
}).code;
}
Example #14
Source File: preprocessor.ts From prettier-plugin-sort-imports with Apache License 2.0 | 5 votes |
export function preprocessor(code: string, options: PrettierOptions) {
const {
importOrderParserPlugins,
importOrder,
importOrderCaseInsensitive,
importOrderSeparation,
importOrderGroupNamespaceSpecifiers,
importOrderSortSpecifiers,
} = options;
const importNodes: ImportDeclaration[] = [];
const parserOptions: ParserOptions = {
sourceType: 'module',
plugins: getExperimentalParserPlugins(importOrderParserPlugins),
};
const ast = babelParser(code, parserOptions);
const interpreter = ast.program.interpreter;
traverse(ast, {
ImportDeclaration(path: NodePath<ImportDeclaration>) {
const tsModuleParent = path.findParent((p) =>
isTSModuleDeclaration(p),
);
if (!tsModuleParent) {
importNodes.push(path.node);
}
},
});
// short-circuit if there are no import declaration
if (importNodes.length === 0) return code;
const allImports = getSortedNodes(importNodes, {
importOrder,
importOrderCaseInsensitive,
importOrderSeparation,
importOrderGroupNamespaceSpecifiers,
importOrderSortSpecifiers,
});
return getCodeFromAst(allImports, code, interpreter);
}
Example #15
Source File: moduleFinder.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 5 votes |
abstract evaluate(path: NodePath<FunctionExpression>): void;
Example #16
Source File: resolveJsxComponent.ts From react-optimized-image with MIT License | 5 votes |
resolveObject = (types: Babel['types'], path: NodePath<JSXElement>, bindings: string[]): Binding | undefined => {
if (bindings.length < 2) {
return;
}
const variableName = bindings[bindings.length - 1];
const object = path.scope.getBinding(bindings[0]);
if (!object) {
return;
}
const program = path.findParent((node) => node.isProgram());
let declarationPath: any = null; // eslint-disable-line
let initializer;
// search for object declaration
program.traverse({
// styles.StyledImg = ...
MemberExpression(exPath: NodePath<MemberExpression>) {
if (exPath.node.property && exPath.node.property.name === variableName) {
const exBindings = resolveMemberExpression(exPath.node);
if (arraysMatch(bindings, exBindings) && exPath.parent.type === 'AssignmentExpression') {
declarationPath = exPath;
initializer = exPath.parent.right;
exPath.stop();
}
}
},
// const styles = { StyledImg: ... }
ObjectProperty(opPath: NodePath<ObjectProperty>) {
if (opPath.node.key && opPath.node.key.type === 'Identifier' && opPath.node.key.name === variableName) {
const exBindings = resolveObjectProperty(
opPath.findParent(() => true) as NodePath<ObjectExpression>,
opPath.node,
);
if (arraysMatch(bindings, exBindings)) {
declarationPath = opPath;
initializer = opPath.node.value;
opPath.stop();
}
}
},
});
if (!declarationPath) {
return;
}
declarationPath = declarationPath as NodePath<MemberExpression>;
// mock a binding
const binding: Partial<Binding> = {
kind: 'const',
scope: declarationPath.scope,
identifier: types.identifier(variableName),
path: {
...(declarationPath as any), // eslint-disable-line
node: types.variableDeclarator(
types.objectPattern([types.objectProperty(types.identifier(variableName), types.identifier(variableName))]),
initializer,
),
},
};
return binding as Binding;
}
Example #17
Source File: index.ts From reskript with MIT License | 5 votes |
insertImportHook = (program: NodePath<babel.types.Program>) => {
const expression = types.importDeclaration(
[types.importDefaultSpecifier(types.identifier('useComponentFile'))],
types.stringLiteral(HOOK_MODULE)
);
const [inserted] = program.unshiftContainer('body', expression);
return inserted;
}
Example #18
Source File: getImpactfulIdentifiers.ts From vidact with MIT License | 5 votes |
export function getImpactfulIdentifiers(
node: t.Node,
scope: Scope,
parentPath: NodePath
) {
const impactfulIdentifiers: [DependencyType, string][] = [];
if (t.isIdentifier(node) && !isElementVar(node.name)) {
impactfulIdentifiers.push(["local", node.name]);
}
traverse(
node,
{
Identifier(p) {
if (t.isMemberExpression(p.container)) {
const parentNode = p.container as t.MemberExpression;
if (t.isIdentifier(parentNode.object)) {
if (p.node.name !== PROP_VAR) {
if (parentNode.object.name === PROP_VAR) {
impactfulIdentifiers.push(["prop", parentNode.property.name]);
} else if (
parentNode.object.name === p.node.name &&
p.node.name !== "Object" &&
!isElementVar(p.node.name)
) {
impactfulIdentifiers.push(["local", p.node.name]);
}
}
}
} else {
if (t.isCallExpression(p.container)) {
const parentNode = p.container as t.CallExpression;
if (
t.isIdentifier(parentNode.callee) &&
parentNode.callee.name === "addPropTransaction"
) {
return;
}
}
if (
t.isObjectProperty(p.container) ||
(parentPath.scope !== p.scope &&
!parentPath.scope.hasBinding(p.node.name))
) {
p.skip();
} else if (!isElementVar(p.node.name)) {
impactfulIdentifiers.push(["local", p.node.name]);
}
}
}
},
scope,
{},
parentPath
);
return impactfulIdentifiers;
}
Example #19
Source File: babelClassEvaluator.ts From react-native-decompiler with GNU Affero General Public License v3.0 | 5 votes |
private classCreatePath?: NodePath<t.VariableDeclarator>;
Example #20
Source File: tsx-wrapper.ts From a18n with MIT License | 4 votes |
wrapCode = (
code: string,
{
filePath,
basePath,
moduleName,
namespace,
checkOnly = false,
moduleNameUpdate = true,
}: WrapOptions & {
filePath?: string
},
): {
output: string
sourceTexts: SourceText[]
} => {
if (code.includes(LIB_IGNORE_FILE)) {
return {
output: code,
sourceTexts: [],
}
}
const ast = parse(code)
const existModuleName = extractModuleName(ast)
const newModuleName: string | undefined = (() => {
if (!moduleName) return existModuleName
if (existModuleName && !moduleNameUpdate) return existModuleName
assert(filePath, 'filePath is required when moduleName is specified')
assert(basePath, 'basePath is required when moduleName is specified')
const { dir, base, name, ext } = path.parse(filePath)
switch (moduleName) {
case 'fileName': {
return name
}
case 'fileDirAndName': {
const relativeDir = relative(basePath, dir)
const lastDir = relativeDir.split(/\/|\\/).pop()
if (lastDir) {
return `${lastDir}/${name}`
} else {
return name
}
}
case 'filePath': {
const relativeDir = relative(basePath, dir)
return (relativeDir + '/' + name)
.split(/\/|\\/)
.filter(Boolean)
.join('/')
}
default:
return assertNever(moduleName)
}
})()
let libUsed = false
const markLibUsed = (): void => {
libUsed = true
}
let libImportStatus = {
imported: false,
required: false,
namespace: undefined as string | undefined,
}
let importStatementCount = 0
let requireCallExpressionCount = 0
const lines = code.split('\n')
let sourceTexts = [] as SourceText[]
const addStaticText = (node: t.Node, text: string): void => {
sourceTexts.push(
toStaticText(node, text, filePath ?? '', lines, newModuleName),
)
}
const addDynamicText = (node: t.Node, parts: string[]) => {
sourceTexts.push(
toDynamicText(node, parts, filePath ?? '', lines, newModuleName),
)
}
traverse(ast, {
enter(path) {
const node = path.node
const lineStart = node.loc ? node.loc.start.line - 1 : -1
const line = lines[lineStart]
const lineAbove = lines[lineStart - 1]
if (
lineAbove?.includes(LIB_IGNORE_LINE) ||
line?.includes(LIB_IGNORE_LINE)
) {
return
}
try {
switch (node.type) {
// '中文' => a18n('中文')
case 'StringLiteral': {
if (needTranslate(node.value)) {
// ignore when it's already: a18n(’中文’)
const parent = path.parent
if (
parent.type === 'CallExpression' &&
t.isIdentifier(parent.callee) &&
parent.callee.name === LIB_IDENTIFIER
) {
markLibUsed()
break
}
// <input placeholder="中文" /> => <input placeholder={a18n('中文') />
if (t.isJSXAttribute(parent)) {
if (checkOnly) {
addStaticText(node, node.value)
} else {
path.replaceWith(
t.jsxExpressionContainer(
t.callExpression(t.identifier(LIB_IDENTIFIER), [
t.stringLiteral(node.value),
]),
),
)
}
markLibUsed()
} else if (
!(t.isProperty(parent) && parent.key === node) &&
!t.isTSLiteralType(parent) &&
!t.isTSEnumMember(parent)
) {
if (checkOnly) {
addStaticText(node, node.value)
} else {
path.replaceWith(
t.callExpression(t.identifier(LIB_IDENTIFIER), [
t.stringLiteral(node.value),
]),
)
}
markLibUsed()
}
}
break
}
// `中文${someVar}` => a18n`中文${someVar}`
case 'TemplateLiteral': {
const { quasis } = node
if (
quasis.some((q) => needTranslate(q.value.cooked ?? q.value.raw))
) {
const parent = path.parent
if (parent.type === 'TaggedTemplateExpression') {
// ignore when it's already wrapped: a18n`中文${someVar}`
if (
t.isIdentifier(parent.tag) &&
parent.tag.name === LIB_IDENTIFIER
) {
markLibUsed()
break
}
// ignore when it's already wrapped: a18n.anyMethod`中文${someVar}`
if (
t.isMemberExpression(parent.tag) &&
t.isIdentifier(parent.tag.object) &&
parent.tag.object.name === LIB_IDENTIFIER
) {
markLibUsed()
break
}
} else {
if (checkOnly) {
addDynamicText(
node,
quasis.map((q) => q.value.cooked ?? q.value.raw),
)
} else {
path.replaceWith(
t.taggedTemplateExpression(
t.identifier(LIB_IDENTIFIER),
node,
),
)
}
markLibUsed()
}
}
break
}
// single jsxtext:
// <div>你好<strong>{userName}</strong></div>
// =>
// <div>{a18n("你好")}<strong>{userName}</strong></div>
//
// multiple jsxtext:
// <div>你好,<strong>{userName}</strong>!!!</div>
// =>
// <div>{a18n.x`你好,${(<strong>{userName}</strong>)}!!!`}</div>
case 'JSXText': {
if (needTranslate(node.value)) {
const parent = path.parent as t.JSXElement | t.JSXFragment
const parentPath = path.parentPath as NodePath<
t.JSXElement | t.JSXFragment
>
const isJSXParent =
t.isJSXElement(parent) || t.isJSXFragment(parent)
if (!isJSXParent) {
break
}
const hasText = (child: t.Node): boolean =>
t.isJSXText(child) && child.value.trim() !== ''
const shouldWrapMultiple =
parent.children.filter(hasText).length > 1
if (shouldWrapMultiple) {
// avoid parent node wrapped multiple times from its jsxtext children,
// only let the first qualified text node trigger wrapping
const theOne = parent.children.find(hasText)
if (theOne && theOne.start !== node.start) {
break
}
const expressions: Array<t.Expression> = []
const quasis: Array<t.TemplateElement> = []
let lastIsText = false
const ensureLastText = () => {
if (!lastIsText) {
quasis.push(
t.templateElement({
raw: '',
cooked: '',
}),
)
}
lastIsText = true
}
;(parent as t.JSXElement | t.JSXFragment).children.forEach(
(child) => {
if (t.isJSXText(child)) {
if (lastIsText) {
const last = quasis[quasis.length - 1]
last.value.cooked =
(last.value.cooked ?? '') + child.value.trim()
last.value.raw = (last.value.cooked ?? '').replace(
/\\|`|\${/g,
'\\$&',
)
} else {
quasis.push(
t.templateElement({
raw:
// Add a backslash before every ‘`’, ‘\’ and ‘${’.
// https://github.com/babel/babel/issues/9242#issuecomment-532529613
child.value.trim().replace(/\\|`|\${/g, '\\$&'),
cooked: child.value.trim(),
}),
)
}
lastIsText = true
} else {
if (t.isJSXExpressionContainer(child)) {
if (t.isJSXEmptyExpression(child.expression)) {
console.warn('[wrap] unexpected JSXEmptyExpression', {
child,
parent,
})
} else {
ensureLastText()
expressions.push(child.expression)
lastIsText = false
}
} else if (t.isJSXSpreadChild(child)) {
console.warn('[wrap] unexpected JSXSpreadChild', {
child,
parent,
})
} else {
ensureLastText()
expressions.push(child)
lastIsText = false
}
}
},
)
ensureLastText()
const children = [
t.jsxExpressionContainer(
t.taggedTemplateExpression(
t.memberExpression(
t.identifier(LIB_IDENTIFIER),
t.identifier(LIB_METHOD_X_IDENTIFIER),
),
t.templateLiteral(quasis, expressions),
),
),
]
if (t.isJSXElement(parent)) {
if (checkOnly) {
addDynamicText(
node,
quasis.map((q) => q.value.cooked ?? q.value.raw),
)
} else {
parentPath.replaceWith(
t.jsxElement(
parent.openingElement,
parent.closingElement,
children,
),
)
}
} else if (t.isJSXFragment(parent)) {
if (checkOnly) {
addDynamicText(
node,
quasis.map((q) => q.value.cooked ?? q.value.raw),
)
} else {
parentPath.replaceWith(
t.jsxFragment(
parent.openingFragment,
parent.closingFragment,
children,
),
)
}
}
} else {
const emptyStart = node.value.match(/^\s*/)![0]
const emptyEnd = node.value.match(/\s*$/)![0]
const nonEmptyText = node.value.trim()
if (checkOnly) {
addStaticText(node, nonEmptyText)
} else {
path.replaceWithMultiple([
t.jsxText(emptyStart),
t.jsxExpressionContainer(t.stringLiteral(nonEmptyText)),
t.jsxText(emptyEnd),
])
}
}
markLibUsed()
}
break
}
case 'ImportDeclaration': {
importStatementCount++
if (node.source.value === LIB_MODULE) {
libImportStatus.imported = true
}
break
}
case 'CallExpression': {
if (t.isIdentifier(node.callee)) {
if (node.callee.name === 'require') {
requireCallExpressionCount++
}
const isRequireLibModule =
node.callee.name === 'require' &&
fromStringLiteral(node.arguments[0]) === LIB_MODULE
if (isRequireLibModule) {
libImportStatus.required = true
}
const isFactoryMethod =
node.callee.name === LIB_FACTORY_IDENTIFIER
if (isFactoryMethod) {
const namespace = fromStringLiteral(node.arguments[0])
libImportStatus.namespace = namespace
}
}
break
}
default:
break
}
} catch (error) {
throw error
}
},
})
if (checkOnly || !libUsed) {
return {
output: code,
sourceTexts,
}
}
let output: string
const shouldKeepFactoryStatement =
(libImportStatus.imported || libImportStatus.required) &&
libImportStatus.namespace == namespace &&
newModuleName === existModuleName
if (shouldKeepFactoryStatement) {
output = print(ast)
} else {
removeImportRequireFactory(ast, lines)
output = print(ast)
const shouldUseImport = importStatementCount >= requireCallExpressionCount
const importDeclarators = namespace
? `{ ${LIB_FACTORY_IDENTIFIER} }`
: LIB_IDENTIFIER
const importStatement = shouldUseImport
? `import ${importDeclarators} from '${LIB_MODULE}'\n`
: `const ${importDeclarators} = require('${LIB_MODULE}')\n`
const factoryStatement = namespace
? newModuleName
? `const a18n = ${LIB_FACTORY_IDENTIFIER}('${namespace}', '${newModuleName}')\n`
: `const a18n = ${LIB_FACTORY_IDENTIFIER}('${namespace}')\n`
: ''
output = importStatement + factoryStatement + output
}
return {
output,
sourceTexts,
}
}
Example #21
Source File: plugin.ts From vidact with MIT License | 4 votes |
function visitFunction(
fnPath: NodePath<t.FunctionDeclaration>,
{ moduleDependencies }: ProgramState
) {
if (!isComponent(fnPath)) {
return fnPath.skip();
}
const variableStatementDependencyManager = new VariableStatementDependencyManager();
const state: ComponentState = {
variablesWithDependencies: new Set(),
needsPropTransaction: false,
looseAssignments: new Set(),
state: [],
variableStatementDependencyManager,
moduleDependencies,
finally: [],
};
// Separate variable declarations with multiple declarators
separateVariableDeclarations(fnPath);
// Rename prop object and move param destruct to body block
normalizePropDefinition(fnPath);
// Convert object pattern assignments to identifier assignments
normalizeObjectPatternAssignment(fnPath);
// Convert `useRef`s to {current} object style
normalizeUseRef(fnPath);
// Convert `useEffect`, `useMemo`, and `useCallback` to proper updaters
scanHooks(fnPath, state);
// Traverse all state and prop references
scanUpdatableValues(fnPath, state);
// Declare variables defined inside updaters in the function scope
if (state.variablesWithDependencies.size > 0) {
fnPath.get("body").unshiftContainer(
"body",
t.variableDeclaration(
"let",
Array.from(state.variablesWithDependencies).map((name) =>
t.variableDeclarator(t.identifier(name))
)
)
);
}
state.looseAssignments.forEach((path) => {
declarationToAssignment(path);
});
for (const statementPath of variableStatementDependencyManager.statements.values()) {
if (!hasAnnotation(statementPath.node, "useEffect")) {
const [updater, callUpdater] = createStatementUpdater(
statementPath,
fnPath.scope
);
statementPath.replaceWith(updater);
statementPath.insertAfter(callUpdater);
}
}
const names: t.Identifier[] = [];
let returnValue: t.Expression;
fnPath.traverse({
JSXElement(path) {
const jsxState: JSXState = {
elements: [],
moduleDependencies,
};
const name = shallowTraverseJSXElement(path.node, jsxState, path.scope);
names.push(name);
jsxState.elements.forEach((definition) => {
const nodePaths: NodePath[] = [];
const nodes = transformerMap[definition.type](definition as any, state);
const nodesList = Array.isArray(nodes) ? nodes : [nodes];
nodesList.forEach((node, i) => {
if (node) {
const parent = path.getStatementParent();
nodePaths[i] = parent.insertBefore(node)[0];
}
});
if (definition.type === "expr") {
const nodePath = nodePaths[1];
getImpactfulIdentifiers(
definition.expression,
path.scope,
path
).forEach(([type, name]) => {
variableStatementDependencyManager.push(
{ type: type as any, name },
{ type: "node", value: nodePath }
);
});
}
if (definition.type === "node") {
const { attributes } = definition;
if (attributes) {
attributes.forEach((_, i) => {
const node = nodes[1 + i];
const nodePath = nodePaths[1 + i];
const impactfulIds = getImpactfulIdentifiers(
node,
path.scope,
path
);
if (impactfulIds.length > 0) {
const [updater, callUpdater] = createStatementUpdater(
nodePath,
fnPath.scope
);
nodePath.replaceWith(updater);
nodePath.insertAfter(callUpdater);
}
impactfulIds.forEach(([type, name]) => {
if (name !== definition.identifier.name) {
variableStatementDependencyManager.push(
{ type: type as any, name },
{ type: "node", value: nodePath }
);
}
});
});
}
}
return nodes;
});
path.skip();
if (path.getStatementParent().isReturnStatement()) {
if (path.scope === fnPath.scope) {
returnValue = name;
path.getStatementParent().remove();
} else {
path.replaceWith(name);
}
} else if (
!t.isObjectProperty(path.container) &&
!t.isVariableDeclarator(path.container)
) {
path.getStatementParent().remove();
} else {
path.replaceWith(name);
}
},
});
fnPath.traverse({ VariableDeclaration: scanForDeepDependencies }, state);
const componentElement = createComponentElement(
returnValue,
createUpdatableUpdater(fnPath.get("body"), state, "prop")
);
state.moduleDependencies.add("propUpdater");
const returnPath: NodePath<t.ReturnStatement> = fnPath
.get("body")
.pushContainer("body", t.returnStatement(componentElement))[0];
if (state.needsPropTransaction) {
fnPath
.get("body")
.unshiftContainer(
"body",
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier(PROP_VAR_TRANSACTION_VAR),
t.newExpression(t.identifier("Map"), [])
),
])
);
}
if (state.state && state.state.length > 0) {
const [internalStateDeclaration, defineUpdater] = createStateDefinition(
state,
fnPath
);
fnPath.get("body").unshiftContainer("body", internalStateDeclaration);
returnPath.insertBefore(defineUpdater);
}
if (state.finally.length > 0) {
returnPath.replaceWith(
t.tryStatement(
t.blockStatement([returnPath.node]),
undefined,
t.blockStatement(state.finally)
)
);
}
fnPath.skip();
}
Example #22
Source File: traverse-module.ts From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
// to record `export {a,b,c} from 'd'`
function traverseJsModule(curModulePath: string, callback: (str: string, items: string[], isImport: boolean) => void) {
const moduleFileContent = fs.readFileSync(curModulePath, {
encoding: 'utf-8',
});
const ast = parser.parse(moduleFileContent, {
sourceType: 'unambiguous',
plugins: resolveBabelSyntaxPlugins(curModulePath),
});
const collectImportItems = (path: NodePath, subModulePath: string) => {
const importItems: string[] = [];
const specifiers = path.get('specifiers');
if (Array.isArray(specifiers) && specifiers.length) {
for (let i = 0; i < specifiers.length; i++) {
const importItem = path.get(`specifiers.${i}`).toString();
const subItem = `${subModulePath}-${importItem}`;
if (exportFromMap[subItem]) {
importItems.push(exportFromMap[subItem]);
}
importItems.push(subItem);
}
}
callback(subModulePath, importItems, true);
};
traverse(ast, {
ImportDeclaration(path) {
// handle syntax `import react from 'react'`
const node = path.get('source.value');
const [subModulePath, visited] = moduleResolver(curModulePath, Array.isArray(node) ? '' : node.node.toString());
if (!subModulePath || visited) {
collectImportItems(path, subModulePath);
return;
}
traverseModule(subModulePath, callback);
collectImportItems(path, subModulePath);
},
CallExpression(path) {
if (path.get('callee').toString() === 'require') {
// handle syntax `const lodash = require('lodash')`
const [subModulePath, visited] = moduleResolver(
curModulePath,
path.get('arguments.0').toString().replace(/['"]/g, ''),
);
if (!subModulePath) {
return;
}
callback(subModulePath, [], visited);
traverseModule(subModulePath, callback);
}
if (path.get('callee').type === 'Import') {
// handle syntax dynamic import `const entry = import('layout/entry')`
try {
const importItem = path.get('arguments.0').toString().replace(/['"]/g, '');
if (importItem && !importItem.includes('`')) {
const [subModulePath, visited] = moduleResolver(curModulePath, importItem);
if (!subModulePath || visited) {
return;
}
callback(subModulePath, [], true);
traverseModule(subModulePath, callback);
}
} catch (error) {
console.log('parse import error', error);
}
}
},
ExportNamedDeclaration(path) {
// handle syntax `export { Copy } from './components/copy'`
if (path.get('source').node) {
const node = path.get('source.value');
const [subModulePath, visited] = moduleResolver(
curModulePath,
Array.isArray(node) ? '' : node.node.toString().replace(/['"]/g, ''),
);
if (!subModulePath || visited) {
return;
}
const specifiers = path.get('specifiers');
const exportItems: string[] = [];
if (Array.isArray(specifiers) && specifiers.length) {
for (let i = 0; i < specifiers.length; i++) {
const importItem = path.get(`specifiers.${i}`).toString();
exportFromMap[`${curModulePath}-${importItem}`] = `${subModulePath}-${importItem}`;
exportItems.push(`${curModulePath}-${importItem}`);
}
}
callback(subModulePath, exportItems, false);
traverseModule(subModulePath, callback);
} else if (path.get('declaration').node) {
// handle export { a, b, c };
const declarations = path.get('declaration.declarations');
const exportItems: string[] = [];
if (Array.isArray(declarations) && declarations.length) {
for (let i = 0; i < declarations.length; i++) {
exportItems.push(`${curModulePath}-${path.get(`declaration.declarations.${i}.id`).toString()}`);
}
}
callback('', exportItems, false);
}
},
});
}
Example #23
Source File: babelPlugin.ts From i18n-helper with MIT License | 4 votes |
i18nPlugin = (transInfo: iTransInfo, i18nConf: iI18nConf): any => {
const JSX_WRAPPER = 'trans';
const OPERATORS = ['===', '==', '!==', '!='];
const { wrapCharacter, wrapperFuncName: T_WRAPPER } = i18nConf;
/**
* 获取 MemberExpression 完整名字
* @param ME MemberExpression
* @param names Array
*/
const getName = (ME: tt.MemberExpression, names: string[]) => {
let { property } = ME;
const { object } = ME;
property = property as tt.Identifier;
names.push(property.name);
if (object.type === 'MemberExpression') {
getName(object, names);
} else if (object.type === 'Identifier') {
names.push((object as tt.Identifier).name);
}
};
const plugin = ({ types: t }: { types: any }) => {
const combine = (value: string) =>
Object.assign(t.StringLiteral(value), {
extra: {
raw: `\'${value.replace("'", "\\'")}\'`,
rawValue: value,
},
});
const replaceLineBreak = (value: string) => {
return value.replace(/\n/g, ' ').trim();
};
const collectWordingInfo = (
value: string,
path: NodePath<tt.Node>,
thisArg: any,
list: iWordInfo[],
) => {
const filename =
thisArg.filename || thisArg.file.opts.filename || 'unknown';
const line = path.node.loc?.start?.line ?? 0;
const wordInfo: iWordInfo = {
key: value,
filename,
line,
};
// wordInfo 结构
// [
// { key: '武强', fileName: 'index.js', line: '91' },
// { key: '你好呀', fileName: 'index.js', line: '191' },
// { key: '武强', fileName: 'index.ts', line: '291' },
// ];
list.push(wordInfo);
};
return {
visitor: {
StringLiteral(path: NodePath<tt.StringLiteral>) {
let { value } = path.node;
value = replaceLineBreak(value);
if (needWrap(wrapCharacter, value)) {
// console.log(`string直接用 replaceLineBreak value:${value}`);
let newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
combine(value),
]);
if (path.parentPath?.node.type === 'JSXAttribute') {
newNode = t.JSXExpressionContainer(newNode);
}
path.replaceWith(newNode);
transInfo.needT = true;
transInfo.wrapCount += 1;
collectWordingInfo(
value,
path as NodePath,
this,
transInfo.wordInfoArray,
);
}
},
TemplateLiteral(path: NodePath<tt.TemplateLiteral>) {
// 全部不包含中文词条则不处理
if (
path.node.quasis.every(
(word) => !needWrap(wrapCharacter, word.value.raw),
)
) {
return;
}
const quasisExpressionsList = (
[].concat(
path.node.quasis as [],
path.node.expressions as [],
) as TLQuasisExpressions
).sort((a, b) => {
const aStart = a.start ?? 0;
const bStart = b.start ?? 0;
return aStart - bStart;
});
let value = '';
let CEIndex = 0;
let BEIndex = 0;
let LEIndex = 0;
let OMEIndex = 0;
let OCEIndex = 0;
const variableList: any = [];
// 组装模板字符串左侧部分
quasisExpressionsList.forEach((templateLiteralItem) => {
const variable: any = {};
switch (templateLiteralItem.type) {
case 'TemplateElement':
// 文字
value += `${replaceLineBreak(
templateLiteralItem.value.cooked ?? '',
)}`;
break;
case 'Identifier': {
// `我有{xx}`
const identifierName = templateLiteralItem.name;
variable.type = 'Identifier';
variable.key = identifierName;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${identifierName}}}`;
break;
}
case 'CallExpression': {
// `我有{obj.xx()}`
// `我有{xx()}`
const { type } = templateLiteralItem.callee;
let callExpressionName = '';
if (type === 'Identifier') {
callExpressionName = (
templateLiteralItem.callee as tt.Identifier
).name;
} else if (type === 'MemberExpression') {
callExpressionName = (
(templateLiteralItem.callee as tt.MemberExpression)
.property as tt.Identifier
).name;
}
variable.type = 'CallExpression';
variable.key = callExpressionName;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${callExpressionName}}}`;
break;
}
case 'MemberExpression': {
let memberExpressionName;
// `我有${obj.xx},${obj[xx]}` Identifier
// `我有${obj['xx']}` StringLiteral
// `我有${array[0]}` NumericLiteral
switch (templateLiteralItem.property.type) {
case 'Identifier':
memberExpressionName = (
templateLiteralItem.property as tt.Identifier
).name;
break;
case 'StringLiteral':
memberExpressionName = (
templateLiteralItem.property as tt.StringLiteral
).value;
break;
case 'NumericLiteral':
memberExpressionName = (
templateLiteralItem.property as tt.NumericLiteral
).value.toString();
break;
case 'MemberExpression':
memberExpressionName = (
templateLiteralItem.property.property as tt.Identifier
).name;
break;
case 'BinaryExpression':
// TODO: 需要看看怎么改
memberExpressionName = 'BinaryExpression';
break;
default:
break;
}
variable.type = 'MemberExpression';
variable.key = memberExpressionName;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${memberExpressionName}}}`;
break;
}
case 'ConditionalExpression': {
// TODO: 目前用ConditionalExpression{index}作为key,可读性不是太好
// 另外如果`武强${index > '这' ? '哈哈' : '呵呵'}呢`
// 如上'这',’哈哈‘,’呵呵‘都不会被包裹。。。
CEIndex += 1;
variable.type = 'ConditionalExpression';
variable.key = `ConditionalExpression${CEIndex}`;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${variable.key}}}`;
break;
}
case 'BinaryExpression': {
BEIndex += 1;
variable.type = 'BinaryExpression';
variable.key = `BinaryExpression${BEIndex}`;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${variable.key}}}`;
break;
}
case 'LogicalExpression': {
LEIndex += 1;
variable.type = 'LogicalExpression';
variable.key = `LogicalExpression${LEIndex}`;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${variable.key}}}`;
break;
}
case 'OptionalMemberExpression': {
OMEIndex += 1;
variable.type = 'OptionalMemberExpression';
variable.key = `OptionalMemberExpression${OMEIndex}`;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${variable.key}}}`;
break;
}
case 'OptionalCallExpression': {
OCEIndex += 1;
variable.type = 'OptionalCallExpression';
variable.key = `OptionalCallExpression${OCEIndex}`;
variable.value = templateLiteralItem;
variableList.push(variable);
value += `{{${variable.key}}}`;
break;
}
default: {
const thisArgs = this as any;
const filename =
thisArgs.filename || thisArgs.file.opts.filename || 'unknown';
const line = path.node.loc?.start?.line ?? 0;
Logger.appendFile(
`[${filename}][${line}]: ${templateLiteralItem.type} 未处理`,
);
transInfo.wrapSuccess = false;
break;
}
}
});
// 组装模板字符串右侧对象
const objArray: any = [];
variableList.map((item: any) => {
let obj;
switch (item.type) {
case 'Identifier': {
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.Identifier,
);
obj.shorthand = true;
break;
}
case 'CallExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.CallExpression,
);
break;
case 'MemberExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.MemberExpression,
);
break;
case 'ConditionalExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.ConditionalExpression,
);
break;
case 'BinaryExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.BinaryExpression,
);
break;
case 'LogicalExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.LogicalExpression,
);
break;
case 'OptionalMemberExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.OptionalMemberExpression,
);
break;
case 'OptionalCallExpression':
obj = t.objectProperty(
t.Identifier(item.key),
item.value as tt.OptionalCallExpression,
);
break;
default:
break;
}
objArray.push(obj);
});
let newNode;
if (objArray.length > 0) {
newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
combine(value),
t.ObjectExpression(objArray),
]);
} else {
// 处理文字全用``包裹的,并没有${}的内容,如 const word = `你好`
newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
combine(value),
]);
}
path.replaceWith(newNode);
transInfo.needT = true;
transInfo.wrapCount += 1;
collectWordingInfo(
value,
path as NodePath,
this,
transInfo.wordInfoArray,
);
},
JSXText(path: NodePath<tt.JSXText>) {
const { node } = path;
const { value } = node;
// jsx内部文字包含中文且不被<trans></trans>包裹
if (
needWrap(wrapCharacter, value)
// needWrap(value) &&
// (
// (path.parentPath.node as tt.JSXElement).openingElement
// .name as tt.JSXIdentifier
// ).name !== JSX_WRAPPER
) {
let newNode;
if (i18nConf.jsx2Trans) {
// 用trans包裹
newNode = t.jsxElement(
t.jsxOpeningElement(t.jsxIdentifier(JSX_WRAPPER), []),
t.jsxClosingElement(t.jsxIdentifier(JSX_WRAPPER)),
[t.jsxText(value)],
true,
);
} else {
// 用 t 包裹
// console.log(`jsx直接用value:${value}`);
newNode = t.CallExpression(t.Identifier(T_WRAPPER), [
combine(replaceLineBreak(value)),
]);
newNode = t.JSXExpressionContainer(newNode);
}
path.replaceWith(newNode);
transInfo.needTrans = true;
transInfo.wrapCount += 1;
collectWordingInfo(
replaceLineBreak(node.value.trim()),
path as NodePath,
this,
transInfo.wordInfoArray,
);
}
},
CallExpression(path: NodePath<tt.CallExpression>) {
const excludeFuncNameRegex =
i18nConf.parsedExcludeWrapperFuncNameRegex;
switch (path.node.callee.type) {
// TODO: 这里目前只能处理 Identifier 方法,后续需要修改
case 'Identifier': {
const { name } = path.node.callee as tt.Identifier;
// 如果是 i18n 包裹的则只收集词条
if (name === T_WRAPPER) {
path.node.arguments
.filter((arg) => arg.type === 'StringLiteral')
.map((sl) => {
const node = sl as tt.StringLiteral;
collectWordingInfo(
node.value.trim(),
path as NodePath,
this,
transInfo.wordInfoArray,
);
});
path.skip();
}
// 处理排除方法 excludeWrapperFuncName
if (excludeFuncNameRegex.test(name)) {
path.skip();
}
break;
}
case 'MemberExpression': {
// 处理排除方法 excludeWrapperFuncName
const names: string[] = [];
const me = path.node.callee as tt.MemberExpression;
getName(me, names);
const MEName = names.reverse().join('.');
if (excludeFuncNameRegex.test(MEName)) {
path.skip();
}
break;
}
default:
break;
}
},
BinaryExpression(path: NodePath<tt.BinaryExpression>) {
if (OPERATORS.includes(path.node.operator)) {
path.skip();
}
},
ImportDeclaration(path: NodePath<tt.ImportDeclaration>) {
if (path.node.source.extra?.raw === i18nConf.parsedImportKey) {
transInfo.needImport = false;
}
},
},
};
};
return plugin;
}