1#!/usr/bin/env node
2import axios from 'axios';
3import chalk from 'chalk';
4import { Command } from 'commander';
5import fs from 'fs-extra';
6import { compile } from 'json-schema-to-typescript';
7import path from 'path';
8import semver from 'semver';
9
10let version: string = '';
11
12const packageJSON = require('../package.json');
13
14const program = new Command(packageJSON.name)
15  .version(packageJSON.version)
16  .arguments('[version]')
17  .usage(`${chalk.green('[version]')} [options]`)
18  .description('Generate TypeScript types from the Expo config JSON schema.')
19  .option('-p, --path <schema-path>', 'Path to a local JSON schema to use.')
20  .action((inputVersion: string, options: any) => {
21    version = inputVersion;
22  })
23  .allowUnknownOption()
24  .parse(process.argv);
25
26async function fetchSchemaAsync(version: string): Promise<Record<string, any>> {
27  const url = `http://exp.host/--/api/v2/project/configuration/schema/${version}`;
28
29  const {
30    data: { data },
31  } = await axios.get(url);
32
33  return data.schema;
34}
35
36(async () => {
37  let schema = {};
38  if (program.path && typeof program.path === 'string') {
39    const filePath = path.resolve(program.path.trim());
40    console.log(`Using local file: "${filePath}"`);
41    try {
42      schema = (await fs.readJSON(filePath)).schema;
43    } catch (error) {
44      console.warn('Failed to read the local JSON schema:');
45      console.error(error);
46      process.exit(1);
47    }
48    if (!schema) {
49      console.error(
50        `The local file "${filePath}" doesn't contain a valid JSON schema with a top-level \`schema\` object`
51      );
52      process.exit(1);
53    }
54  } else {
55    if (typeof version === 'string') {
56      version = version.trim();
57    }
58
59    if (!version) {
60      // @ts-ignore
61      version = semver.parse(packageJSON.version).major;
62      console.log('Using package version: ' + version);
63    }
64    let parsedVersion = version;
65    if (parsedVersion !== 'unversioned') {
66      parsedVersion += '.0.0';
67    } else {
68      parsedVersion = parsedVersion.toUpperCase();
69    }
70
71    schema = await fetchSchemaAsync(parsedVersion);
72  }
73
74  const ts = await compile(schema as any, 'ExpoConfig', {
75    bannerComment: `/* tslint:disable */\n/**\n* The standard Expo config object defined in \`app.config.js\` files.\n*/`,
76    unknownAny: false,
77  });
78  const filepath = `src/ExpoConfig.ts`;
79  fs.ensureDirSync(path.dirname(filepath));
80  await fs.writeFile(filepath, ts, 'utf8');
81})();
82