xref: /expo/tools/src/commands/GenerateSDKDocs.ts (revision 168ee43f)
1import JsonFile from '@expo/json-file';
2import spawnAsync from '@expo/spawn-async';
3import chalk from 'chalk';
4import fs from 'fs-extra';
5import path from 'path';
6
7import * as Directories from '../Directories';
8import { transformFileAsync } from '../Transforms';
9
10const EXPO_DIR = Directories.getExpoRepositoryRootDir();
11const DOCS_DIR = path.join(EXPO_DIR, 'docs');
12const SDK_DOCS_DIR = path.join(DOCS_DIR, 'pages', 'versions');
13const STATIC_EXAMPLES_DIR = path.join(DOCS_DIR, 'public', 'static', 'examples');
14const STATIC_API_DATA_DIR = path.join(DOCS_DIR, 'public', 'static', 'data');
15
16async function action(options) {
17  const { sdk, updateReactNativeDocs } = options;
18
19  if (!sdk) {
20    throw new Error('Must run with `--sdk SDK_VERSION`.');
21  }
22
23  if (updateReactNativeDocs) {
24    const reactNativeWebsiteDir = path.join(DOCS_DIR, 'react-native-website');
25    const reactNativePackageJsonPath = path.join(
26      EXPO_DIR,
27      'react-native-lab',
28      'react-native',
29      'package.json'
30    );
31    const reactNativeVersion = await JsonFile.getAsync(reactNativePackageJsonPath, 'version', null);
32
33    if (!reactNativeVersion) {
34      throw new Error(`React Native version not found at ${reactNativePackageJsonPath}`);
35    }
36
37    console.log(`Updating ${chalk.cyan('react-native-website')} submodule...`);
38
39    await spawnAsync('git', ['checkout', 'main'], {
40      cwd: reactNativeWebsiteDir,
41    });
42
43    await spawnAsync('git', ['pull'], {
44      cwd: reactNativeWebsiteDir,
45    });
46
47    console.log(`Importing React Native docs to ${chalk.yellow('unversioned')} directory...\n`);
48
49    await fs.remove(path.join(SDK_DOCS_DIR, 'unversioned', 'react-native'));
50
51    await spawnAsync('et', ['update-react-native-docs', '--sdk', 'unversioned'], {
52      stdio: 'inherit',
53      cwd: DOCS_DIR,
54    });
55  }
56
57  const versionDirectory = `v${sdk}`;
58  const targetSdkDirectory = path.join(SDK_DOCS_DIR, versionDirectory);
59  const targetExampleDirectory = path.join(STATIC_EXAMPLES_DIR, versionDirectory);
60  const targetAPIDataDirectory = path.join(STATIC_API_DATA_DIR, versionDirectory);
61
62  if (await fs.pathExists(targetSdkDirectory)) {
63    console.log(
64      chalk.magenta(versionDirectory),
65      'directory already exists. Skipping copy operation.'
66    );
67  } else {
68    console.log(
69      `Copying ${chalk.yellow('unversioned')} docs to ${chalk.yellow(
70        versionDirectory
71      )} directory...`
72    );
73
74    await fs.copy(path.join(SDK_DOCS_DIR, 'unversioned'), targetSdkDirectory);
75
76    // Version the sourcecode URLs for the API pages
77    const apiPages = await fs.readdir(path.join(targetSdkDirectory, 'sdk'));
78    await Promise.all(
79      apiPages.map(async (api) => {
80        const apiFilePath = path.join(targetSdkDirectory, 'sdk', api);
81        await transformFileAsync(apiFilePath, [
82          {
83            find: /(sourceCodeUrl:.*?\/tree\/)(main)(\/packages[^\n]*)/,
84            replaceWith: `$1sdk-${sdk.substring(0, 2)}$3`,
85          },
86        ]);
87      })
88    );
89  }
90
91  if (await fs.pathExists(targetExampleDirectory)) {
92    console.log(
93      chalk.magenta(versionDirectory),
94      'examples directory already exists. Skipping copy operation.'
95    );
96  } else {
97    console.log(
98      `Copying ${chalk.yellow('unversioned')} static examples to ${chalk.yellow(
99        versionDirectory
100      )} directory…`
101    );
102
103    await fs.copy(path.join(STATIC_EXAMPLES_DIR, 'unversioned'), targetExampleDirectory);
104  }
105
106  if (await fs.pathExists(targetAPIDataDirectory)) {
107    console.log(
108      chalk.magenta(versionDirectory),
109      'API data directory already exists. Skipping copy operation.'
110    );
111  } else {
112    console.log(
113      `Copying ${chalk.yellow('unversioned')} generated API files to ${chalk.yellow(
114        versionDirectory
115      )} directory…`
116    );
117
118    await fs.copy(path.join(STATIC_API_DATA_DIR, 'unversioned'), targetAPIDataDirectory);
119  }
120
121  console.log(
122    `\nDocs version ${chalk.red(
123      sdk
124    )} created successfully. By default, it will not be included in the production build.` +
125      `\nWhen the new version is ready to deploy, set version to ${chalk.red(
126        sdk
127      )} in ${chalk.yellow('docs/package.json')}`
128  );
129}
130
131export default (program) => {
132  program
133    .command('generate-sdk-docs')
134    .option('--sdk <string>', 'SDK version of docs to generate.')
135    .option('--update-react-native-docs', 'Whether to update React Native docs.')
136    .description(`Copies unversioned docs and static examples to SDK-specific folder.`)
137    .asyncAction(action);
138};
139