Skip to content

Instantly share code, notes, and snippets.

@evanpurkhiser
Created August 14, 2024 03:42
Show Gist options
  • Save evanpurkhiser/83f2f0098f7b215894342903bd3b02d6 to your computer and use it in GitHub Desktop.
Save evanpurkhiser/83f2f0098f7b215894342903bd3b02d6 to your computer and use it in GitHub Desktop.
import { readFileSync } from "node:fs";
import type { API, FileInfo } from "jscodeshift";
const typeFiles = [
"alerts",
"auth",
"breadcrumbs",
"controlSiloOrganization",
"core",
"debugFiles",
"debugImage",
"echarts",
"event",
"experiments",
"group",
"hooks",
"index",
"integrations",
"metrics",
"notificationActions",
"onboarding",
"organization",
"project",
"relay",
"release",
"sampling",
"sandbox",
"sessions",
"sourceMaps",
"stacktrace",
"system",
"user",
"utils",
"views",
];
let identifierToTypeFile: Record<string, string> | null = null;
export default function transformer(file: FileInfo, api: API) {
const j = api.jscodeshift;
// Create the mapping of type identifiers to the type file they belong to. We
// only need to do this once so store it in a global variable once we're done
if (identifierToTypeFile === null) {
const sentryRoot = "/Users/evan/Coding/sentry/static/app/";
const identifiersPerType = typeFiles.map((typeFile) => {
const ast = j(
readFileSync(`${sentryRoot}types/${typeFile}.tsx`, {
encoding: "utf8",
flag: "r",
}),
);
const identifiers: string[] = [];
ast.find(j.ExportNamedDeclaration).forEach((path) => {
if (!path.node.declaration) {
return;
}
if (
"id" in path.node.declaration &&
path.node.declaration.id &&
"name" in path.node.declaration.id
) {
identifiers.push(path.node.declaration.id.name);
}
});
return [typeFile, identifiers] as const;
});
identifierToTypeFile = Object.fromEntries(
identifiersPerType.flatMap(([typeFile, identifiers]) =>
identifiers.map((id) => [id, typeFile]),
),
);
}
// Find type imports in every file and break them apart into multiple
const ast = j(file.source);
const typeImport = ast
.find(j.ImportDeclaration)
.filter((path) => path.node.source.value === "sentry/types");
typeImport.forEach((path) => {
const typeGroups: Record<string, string[]> = {};
path.node.specifiers?.forEach((s) => {
const id = s.type === "ImportSpecifier" ? s.imported.name : undefined;
const typeFile = id ? identifierToTypeFile?.[id] : undefined;
if (id === undefined || typeFile === undefined) {
console.log(`TYPE MISSING FILE MAPPING: ${id}`);
return;
}
typeGroups[typeFile] ??= [];
typeGroups[typeFile].push(id);
});
const imports = Object.entries(typeGroups).map(([typeFile, imports]) =>
j.importDeclaration(
imports.map((i) => j.importSpecifier(j.identifier(i))),
j.stringLiteral(`sentry/types/${typeFile}`),
),
);
const first = imports.shift();
// Replace the `sentry/types` import with the first and add the rest after
path.replace(first);
imports.forEach((i) => path.insertAfter(i));
});
return ast.toSource();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment