extract-ios-device-names.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /* eslint-env node */
  2. import fs from 'node:fs';
  3. import path from 'node:path';
  4. import prettier from 'prettier';
  5. // joining path of directory
  6. const tmpOutputPath = path.join(
  7. __dirname,
  8. '../static/app/constants/ios-device-list.tmp.tsx'
  9. );
  10. const outputPath = path.join(__dirname, '../static/app/constants/ios-device-list.tsx');
  11. const directoryPath = path.join(__dirname, '../node_modules/ios-device-list/');
  12. async function getDefinitionFiles(): Promise<string[]> {
  13. const files: string[] = [];
  14. const maybeJSONFiles = await fs.readdirSync(directoryPath);
  15. // listing all files using forEach
  16. maybeJSONFiles.forEach(file => {
  17. if (!file.endsWith('.json') || file === 'package.json') {
  18. return;
  19. }
  20. files.push(path.join(path.resolve(directoryPath), file));
  21. });
  22. return files;
  23. }
  24. type Generation = string;
  25. type Identifier = string;
  26. type Mapping = Record<Identifier, Generation>;
  27. function collectDefinitions(files: string[]): Mapping {
  28. const definitions: Mapping = {};
  29. const queue = [...files];
  30. while (queue.length > 0) {
  31. const file = queue.pop();
  32. if (!file) {
  33. throw new Error('Empty queue');
  34. }
  35. const contents = fs.readFileSync(file, 'utf-8');
  36. const content = JSON.parse(contents);
  37. if (typeof content?.[0]?.Identifier === 'undefined') {
  38. continue;
  39. }
  40. for (let i = 0; i < content.length; i++) {
  41. if (!content[i].Identifier) {
  42. continue;
  43. }
  44. definitions[content[i].Identifier] = content[i].Generation;
  45. }
  46. }
  47. return definitions;
  48. }
  49. const HEADER = `
  50. // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
  51. // generated using scripts/extract-ios-device-names.ts as part of build step.
  52. // the purpose of the script is to extract only the iOS information that Sentry cares about
  53. // and discard the rest of the JSON so we do not end up bloating bundle size.
  54. `;
  55. const template = (contents: string) => {
  56. return `
  57. ${HEADER}
  58. const iOSDeviceMapping: Record<string, string> = ${contents}
  59. export {iOSDeviceMapping}
  60. `;
  61. };
  62. const formatOutput = async (unformatted: string) => {
  63. const config = await prettier.resolveConfig(outputPath);
  64. if (config) {
  65. return prettier.format(unformatted, {...config, parser: 'typescript'});
  66. }
  67. return unformatted;
  68. };
  69. export async function extractIOSDeviceNames() {
  70. const files = await getDefinitionFiles();
  71. const definitions = collectDefinitions(files);
  72. const formatted = await formatOutput(
  73. template(JSON.stringify(definitions, undefined, 2))
  74. );
  75. // All exit code has to synchronous
  76. const cleanup = () => {
  77. if (fs.existsSync(tmpOutputPath)) {
  78. fs.unlinkSync(tmpOutputPath);
  79. }
  80. };
  81. process.on('exit', cleanup);
  82. process.on('SIGINT', () => {
  83. cleanup();
  84. process.exit(1);
  85. });
  86. // Write to tmp output path
  87. fs.writeFileSync(tmpOutputPath, formatted);
  88. // Rename the file (atomic)
  89. fs.renameSync(tmpOutputPath, outputPath);
  90. }