Files
sentinel/scripts/extract-ios-device-names.ts
2024-02-15 17:01:50 +00:00

109 lines
2.9 KiB
TypeScript

/* eslint-env node */
import {existsSync, unlinkSync} from 'node:fs';
import fs from 'node:fs/promises';
import path from 'node:path';
// joining path of directory
const tmpOutputPath = path.join(
__dirname,
'../static/app/constants/ios-device-list.tmp.tsx'
);
const outputPath = path.join(__dirname, '../static/app/constants/ios-device-list.tsx');
const directoryPath = path.join(__dirname, '../node_modules/ios-device-list/');
type Generation = string;
type Identifier = string;
type Mapping = Record<Identifier, Generation>;
async function getDefinitionFiles(): Promise<string[]> {
const files: string[] = [];
const maybeJSONFiles = await fs.readdir(directoryPath);
// listing all files using forEach
for (const file of maybeJSONFiles) {
if (!file.endsWith('.json') || file === 'package.json') {
continue;
}
files.push(path.join(path.resolve(directoryPath), file));
}
return files;
}
async function collectDefinitions(files: string[]): Promise<Mapping> {
const definitions: Mapping = {};
const queue = [...files];
while (queue.length > 0) {
const file = queue.pop();
if (!file) {
throw new Error('Empty queue');
}
const contents = await fs.readFile(file, 'utf8');
const content = JSON.parse(contents);
if (typeof content?.[0]?.Identifier === 'undefined') {
continue;
}
for (let i = 0; i < content.length; i++) {
if (content[i].Identifier) {
definitions[content[i].Identifier] = content[i].Generation;
}
}
}
return definitions;
}
const HEADER = `
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// generated using scripts/extract-ios-device-names.ts as part of build step.
// the purpose of the script is to extract only the iOS information that Sentry cares about
// and discard the rest of the JSON so we do not end up bloating bundle size.
`;
// The formatting issues will be picked up by the CI and can quickly be fixed by
// running `yarn fix` command that triggers the linter and formatter.
const template = (contents: string) => {
return `
${HEADER}
const iOSDeviceMapping: Record<string, string> = ${contents}
export {iOSDeviceMapping}
`;
};
async function run() {
const files = await getDefinitionFiles();
const definitions = await collectDefinitions(files);
const formatted = template(JSON.stringify(definitions, undefined, 2));
// All exit code has to synchronous
function cleanup() {
if (existsSync(tmpOutputPath)) {
unlinkSync(tmpOutputPath);
}
}
process.once('exit', cleanup);
process.once('SIGINT', () => {
cleanup();
process.exit(1);
});
// Write to tmp output path
await fs.writeFile(tmpOutputPath, formatted);
// Rename the file (atomic)
await fs.rename(tmpOutputPath, outputPath);
}
run()
// eslint-disable-next-line no-console
.catch(error => console.error(`Failed to run extract-ios-device-names`, error));