xref: /expo/tools/src/commands/CreateUnimodule.ts (revision a272999e)
1import { Command } from '@expo/commander';
2import JsonFile from '@expo/json-file';
3import chalk from 'chalk';
4import * as path from 'path';
5
6import { PACKAGES_DIR, EXPO_DIR } from '../Constants';
7import { spawnAsync } from '../Utils';
8import generateModuleAsync from '../generate-module/generateModuleAsync';
9
10type ActionOptions = {
11  name: string;
12  template?: string;
13  useLocalTemplate?: boolean;
14};
15
16async function setupExpoModuleScripts(unimoduleDirectory) {
17  const packageJsonPath = path.join(unimoduleDirectory, 'package.json');
18  const packageJson = new JsonFile(packageJsonPath);
19  const moduleScriptsVersion = (await JsonFile.getAsync(
20    path.join(PACKAGES_DIR, 'expo-module-scripts', 'package.json'),
21    'version',
22    ''
23  )) as string;
24
25  console.log(`Installing ${chalk.bold.green('expo-module-scripts')}...`);
26
27  await spawnAsync('yarn', ['add', '--dev', `expo-module-scripts@^${moduleScriptsVersion}`], {
28    cwd: unimoduleDirectory,
29  });
30
31  console.log(`Setting up ${chalk.magenta(path.relative(EXPO_DIR, packageJsonPath))}...`);
32
33  await packageJson.setAsync('scripts', {
34    build: 'expo-module build',
35    clean: 'expo-module clean',
36    lint: 'expo-module lint',
37    test: 'expo-module test',
38    prepare: 'expo-module prepare',
39    prepublishOnly: 'expo-module prepublishOnly',
40    'expo-module': 'expo-module',
41  });
42
43  await packageJson.setAsync('repository', {
44    type: 'git',
45    url: 'https://github.com/expo/expo.git',
46    directory: path.relative(EXPO_DIR, unimoduleDirectory),
47  });
48
49  await packageJson.setAsync('bugs', {
50    url: 'https://github.com/expo/expo/issues',
51  });
52
53  await packageJson.setAsync('jest', {
54    preset: 'expo-module-scripts/ios',
55  });
56
57  // `expo generate-module` left some junk fields in package.json
58  // TODO(@tsapeta): Probably these keys should be deleted by CLI, but I'd like to do this separately since it needs some other changes as well.
59  await packageJson.deleteKeysAsync(['gitHead', '_resolved', '_integrity', '_from']);
60}
61
62async function action(options: ActionOptions) {
63  if (!options.name) {
64    throw new Error('Missing unimodule name. Run with `--name <string>`.');
65  }
66
67  const unimoduleDirectory = path.join(PACKAGES_DIR, options.name);
68
69  await generateModuleAsync(unimoduleDirectory, options);
70
71  await setupExpoModuleScripts(unimoduleDirectory);
72}
73
74export default (program: Command) => {
75  program
76    .command('create-unimodule')
77    .alias('cu')
78    .description('Creates a new unimodule under the `packages` folder.')
79    .option('-n, --name <string>', 'Name of the package to create.', null)
80    .option(
81      '--use-local-template',
82      'Uses local `packages/expo-module-template` instead of the one published to NPM. Ignored when -t option is used.'
83    )
84    .option(
85      '-t, --template <string>',
86      'Local directory or npm package containing template for unimodule'
87    )
88    .asyncAction(action);
89};
90