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