@tobiastengler/create-relay-app
Version:
Easy configuration of Relay for existing projects
99 lines (98 loc) • 3.91 kB
JavaScript
import generate from "@babel/generator";
import { parse } from "@babel/parser";
import t from "@babel/types";
import { format } from "prettier";
export function parseAst(code) {
return parse(code, {
sourceType: "module",
plugins: ["typescript", "jsx"],
});
}
export function astToString(ast, oldCode) {
return generate.default(ast, { retainLines: true }, oldCode).code;
}
export function printAst(ast, oldCode) {
const newCode = astToString(ast, oldCode);
return prettifyCode(newCode);
}
export function prettifyCode(code) {
return format(code, {
bracketSameLine: false,
endOfLine: "auto",
parser: "babel-ts",
});
}
export function insertNamedImport(path, importName, packageName) {
return insertNamedImports(path, [importName], packageName)[0];
}
export function insertNamedImports(path, imports, packageName) {
const program = path.findParent((p) => p.isProgram());
const identifiers = [];
const missingImports = [];
for (const namedImport of imports) {
const importIdentifier = t.identifier(namedImport);
const existingImport = getNamedImport(program, namedImport, packageName);
if (!!existingImport) {
identifiers.push(importIdentifier);
continue;
}
missingImports.push(namedImport);
}
let importDeclaration;
const isFirstImportFromPackage = missingImports.length === imports.length;
if (isFirstImportFromPackage) {
importDeclaration = t.importDeclaration([], t.stringLiteral(packageName));
}
else {
importDeclaration = getImportDeclaration(program, packageName);
}
for (const namedImport of missingImports) {
const importIdentifier = t.identifier(namedImport);
const newImport = t.importSpecifier(t.cloneNode(importIdentifier), importIdentifier);
importDeclaration.specifiers.push(newImport);
identifiers.push(importIdentifier);
}
if (isFirstImportFromPackage) {
// Insert import at start of file.
program.node.body.unshift(importDeclaration);
}
return identifiers;
}
export function insertDefaultImport(path, importName, packageName) {
const importIdentifier = t.identifier(importName);
const program = path.findParent((p) => p.isProgram());
const existingImport = getDefaultImport(program, importName, packageName);
if (!!existingImport) {
return importIdentifier;
}
const importDeclaration = t.importDeclaration([t.importDefaultSpecifier(t.cloneNode(importIdentifier))], t.stringLiteral(packageName));
// Insert import at start of file.
program.node.body.unshift(importDeclaration);
return importIdentifier;
}
function getImportDeclaration(path, packageName) {
return path.node.body.find((s) => t.isImportDeclaration(s) && s.source.value === packageName);
}
export function getNamedImport(path, importName, packageName) {
return path.node.body.find((s) => t.isImportDeclaration(s) &&
s.source.value === packageName &&
s.specifiers.some((sp) => t.isImportSpecifier(sp) && sp.local.name === importName));
}
function getDefaultImport(path, importName, packageName) {
return path.node.body.find((s) => t.isImportDeclaration(s) &&
s.source.value === packageName &&
s.specifiers.some((sp) => t.isImportDefaultSpecifier(sp) && sp.local.name === importName));
}
export function mergeProperties(existingProps, newProps) {
let existingCopy = [...existingProps];
for (const prop of newProps) {
const existingIndex = existingCopy.findIndex((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && t.isIdentifier(prop.key) && p.key.name === prop.key.name);
if (existingIndex !== -1) {
existingCopy[existingIndex] = prop;
}
else {
existingCopy.push(prop);
}
}
return existingCopy;
}