UNPKG

expo-modules-autolinking

Version:
213 lines (199 loc) 7.49 kB
import commander from 'commander'; import path from 'path'; import { findModulesAsync, generateModulesProviderAsync, generatePackageListAsync, getProjectPackageJsonPathAsync, mergeLinkingOptionsAsync, resolveExtraBuildDependenciesAsync, resolveModulesAsync, verifySearchResults, } from './autolinking'; import { type RNConfigCommandOptions, createReactNativeConfigAsync } from './reactNativeConfig'; import type { GenerateModulesProviderOptions, GenerateOptions, ResolveOptions, SearchOptions, SearchResults, } from './types'; /** * Registers a command that only searches for available expo modules. */ function registerSearchCommand<OptionsType extends SearchOptions>( commandName: string, fn: (search: SearchResults, options: OptionsType) => any ) { return commander .command(`${commandName} [paths...]`) .option<string[] | null>( '-i, --ignore-paths <ignorePaths...>', 'Paths to ignore when looking up for modules.', (value, previous) => (previous ?? []).concat(value) ) .option<string[] | null>( '-e, --exclude <exclude...>', 'Package names to exclude when looking up for modules.', (value, previous) => (previous ?? []).concat(value) ) .option( '-p, --platform [platform]', 'The platform that the resulting modules must support. Available options: "apple", "android"', 'apple' ) .option('--silent', 'Silence resolution warnings') .addOption( new commander.Option( '--project-root <projectRoot>', 'The path to the root of the project' ).default(process.cwd(), 'process.cwd()') ) .option( '--only-project-deps', 'For a monorepo, include only modules that are the project dependencies.', true ) .option('--no-only-project-deps', 'Opposite of --only-project-deps', false) .action(async (searchPaths, providedOptions) => { const options = await mergeLinkingOptionsAsync<OptionsType>( searchPaths.length > 0 ? { ...providedOptions, searchPaths, } : providedOptions ); const searchResults = await findModulesAsync(options); return await fn(searchResults, options); }); } /** * Registers a command that searches for modules and then resolves them for specific platform. */ function registerResolveCommand<OptionsType extends ResolveOptions>( commandName: string, fn: (search: SearchResults, options: OptionsType) => any ) { return registerSearchCommand<OptionsType>(commandName, fn); } /** * Registry the `react-native-config` command. */ function registerReactNativeConfigCommand() { return commander .command('react-native-config [paths...]') .option( '-p, --platform [platform]', 'The platform that the resulting modules must support. Available options: "android", "ios"', 'ios' ) .addOption( new commander.Option( '--project-root <projectRoot>', 'The path to the root of the project' ).default(process.cwd(), 'process.cwd()') ) .option<boolean>('-j, --json', 'Output results in the plain JSON format.', () => true, false) .action(async (searchPaths, providedOptions) => { if (!['android', 'ios'].includes(providedOptions.platform)) { throw new Error(`Unsupported platform: ${providedOptions.platform}`); } const projectRoot = path.dirname( await getProjectPackageJsonPathAsync(providedOptions.projectRoot) ); const linkingOptions = await mergeLinkingOptionsAsync<SearchOptions>( searchPaths.length > 0 ? { ...providedOptions, projectRoot, searchPaths, } : { ...providedOptions, projectRoot, } ); const options: RNConfigCommandOptions = { platform: linkingOptions.platform, projectRoot, searchPaths: linkingOptions.searchPaths, }; const results = await createReactNativeConfigAsync(options); if (providedOptions.json) { console.log(JSON.stringify(results)); } else { console.log(require('util').inspect(results, false, null, true)); } }); } module.exports = async function (args: string[]) { // Searches for available expo modules. registerSearchCommand<SearchOptions & { json?: boolean }>('search', async (results, options) => { if (options.json) { console.log(JSON.stringify(results)); } else { console.log(require('util').inspect(results, false, null, true)); } }).option<boolean>('-j, --json', 'Output results in the plain JSON format.', () => true, false); // Checks whether there are no resolving issues in the current setup. registerSearchCommand('verify', (results, options) => { const numberOfDuplicates = verifySearchResults(results, options); if (!numberOfDuplicates) { console.log('✅ Everything is fine!'); } }); // Searches for available expo modules and resolves the results for given platform. registerResolveCommand('resolve', async (results, options) => { const modules = await resolveModulesAsync(results, options); const extraDependencies = await resolveExtraBuildDependenciesAsync(options); if (options.json) { console.log(JSON.stringify({ extraDependencies, modules })); } else { console.log(require('util').inspect({ extraDependencies, modules }, false, null, true)); } }).option<boolean>('-j, --json', 'Output results in the plain JSON format.', () => true, false); // Generates a source file listing all packages to link. // It's deprecated for apple platforms, use `generate-modules-provider` instead. registerResolveCommand<GenerateOptions>('generate-package-list', async (results, options) => { const modules = options.empty ? [] : await resolveModulesAsync(results, options); generatePackageListAsync(modules, options); }) .option( '-t, --target <path>', 'Path to the target file, where the package list should be written to.' ) .option( '-n, --namespace <namespace>', 'Java package name under which the package list should be placed.' ) .option( '--empty', 'Whether to only generate an empty list. Might be used when the user opts-out of autolinking.', false ); // Generates a source file listing all packages to link in the runtime. registerResolveCommand<GenerateModulesProviderOptions>( 'generate-modules-provider', async (results, options) => { const packages = options.packages ?? []; const modules = await resolveModulesAsync(results, options); const filteredModules = modules.filter((module) => packages.includes(module.packageName)); generateModulesProviderAsync(filteredModules, options); } ) .option( '-t, --target <path>', 'Path to the target file, where the package list should be written to.' ) .option('--entitlement <path>', 'Path to the Apple code signing entitlements file.') .option( '-p, --packages <packages...>', 'Names of the packages to include in the generated modules provider.' ); registerReactNativeConfigCommand(); await commander .version(require('expo-modules-autolinking/package.json').version) .description('CLI command that searches for Expo modules to autolink them.') .parseAsync(args, { from: 'user' }); };