123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow
- * @format
- */
- 'use strict';
- const BatchedBridge = require('../BatchedBridge/BatchedBridge');
- const BugReporting = require('../BugReporting/BugReporting');
- const ReactNative = require('../Renderer/shims/ReactNative');
- const SceneTracker = require('../Utilities/SceneTracker');
- const infoLog = require('../Utilities/infoLog');
- const invariant = require('invariant');
- const renderApplication = require('./renderApplication');
- const createPerformanceLogger = require('../Utilities/createPerformanceLogger');
- import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
- import NativeHeadlessJsTaskSupport from './NativeHeadlessJsTaskSupport';
- import HeadlessJsTaskError from './HeadlessJsTaskError';
- type Task = (taskData: any) => Promise<void>;
- export type TaskProvider = () => Task;
- type TaskCanceller = () => void;
- type TaskCancelProvider = () => TaskCanceller;
- export type ComponentProvider = () => React$ComponentType<any>;
- export type ComponentProviderInstrumentationHook = (
- component: ComponentProvider,
- scopedPerformanceLogger: IPerformanceLogger,
- ) => React$ComponentType<any>;
- export type AppConfig = {
- appKey: string,
- component?: ComponentProvider,
- run?: Function,
- section?: boolean,
- ...
- };
- export type Runnable = {
- component?: ComponentProvider,
- run: Function,
- ...
- };
- export type Runnables = {[appKey: string]: Runnable, ...};
- export type Registry = {
- sections: Array<string>,
- runnables: Runnables,
- ...
- };
- export type WrapperComponentProvider = any => React$ComponentType<*>;
- const runnables: Runnables = {};
- let runCount = 1;
- const sections: Runnables = {};
- const taskProviders: Map<string, TaskProvider> = new Map();
- const taskCancelProviders: Map<string, TaskCancelProvider> = new Map();
- let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook = (
- component: ComponentProvider,
- ) => component();
- let wrapperComponentProvider: ?WrapperComponentProvider;
- let showArchitectureIndicator = false;
- /**
- * `AppRegistry` is the JavaScript entry point to running all React Native apps.
- *
- * See https://reactnative.dev/docs/appregistry.html
- */
- const AppRegistry = {
- setWrapperComponentProvider(provider: WrapperComponentProvider) {
- wrapperComponentProvider = provider;
- },
- enableArchitectureIndicator(enabled: boolean): void {
- showArchitectureIndicator = enabled;
- },
- registerConfig(config: Array<AppConfig>): void {
- config.forEach(appConfig => {
- if (appConfig.run) {
- AppRegistry.registerRunnable(appConfig.appKey, appConfig.run);
- } else {
- invariant(
- appConfig.component != null,
- 'AppRegistry.registerConfig(...): Every config is expected to set ' +
- 'either `run` or `component`, but `%s` has neither.',
- appConfig.appKey,
- );
- AppRegistry.registerComponent(
- appConfig.appKey,
- appConfig.component,
- appConfig.section,
- );
- }
- });
- },
- /**
- * Registers an app's root component.
- *
- * See https://reactnative.dev/docs/appregistry.html#registercomponent
- */
- registerComponent(
- appKey: string,
- componentProvider: ComponentProvider,
- section?: boolean,
- ): string {
- let scopedPerformanceLogger = createPerformanceLogger();
- runnables[appKey] = {
- componentProvider,
- run: appParameters => {
- renderApplication(
- componentProviderInstrumentationHook(
- componentProvider,
- scopedPerformanceLogger,
- ),
- appParameters.initialProps,
- appParameters.rootTag,
- wrapperComponentProvider && wrapperComponentProvider(appParameters),
- appParameters.fabric,
- showArchitectureIndicator,
- scopedPerformanceLogger,
- appKey === 'LogBox',
- );
- },
- };
- if (section) {
- sections[appKey] = runnables[appKey];
- }
- return appKey;
- },
- registerRunnable(appKey: string, run: Function): string {
- runnables[appKey] = {run};
- return appKey;
- },
- registerSection(appKey: string, component: ComponentProvider): void {
- AppRegistry.registerComponent(appKey, component, true);
- },
- getAppKeys(): Array<string> {
- return Object.keys(runnables);
- },
- getSectionKeys(): Array<string> {
- return Object.keys(sections);
- },
- getSections(): Runnables {
- return {
- ...sections,
- };
- },
- getRunnable(appKey: string): ?Runnable {
- return runnables[appKey];
- },
- getRegistry(): Registry {
- return {
- sections: AppRegistry.getSectionKeys(),
- runnables: {...runnables},
- };
- },
- setComponentProviderInstrumentationHook(
- hook: ComponentProviderInstrumentationHook,
- ) {
- componentProviderInstrumentationHook = hook;
- },
- /**
- * Loads the JavaScript bundle and runs the app.
- *
- * See https://reactnative.dev/docs/appregistry.html#runapplication
- */
- runApplication(appKey: string, appParameters: any): void {
- if (appKey !== 'LogBox') {
- const msg =
- 'Running "' + appKey + '" with ' + JSON.stringify(appParameters);
- infoLog(msg);
- BugReporting.addSource(
- 'AppRegistry.runApplication' + runCount++,
- () => msg,
- );
- }
- invariant(
- runnables[appKey] && runnables[appKey].run,
- `"${appKey}" has not been registered. This can happen if:\n` +
- '* Metro (the local dev server) is run from the wrong folder. ' +
- 'Check if Metro is running, stop it and restart it in the current project.\n' +
- "* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.",
- );
- SceneTracker.setActiveScene({name: appKey});
- runnables[appKey].run(appParameters);
- },
- /**
- * Stops an application when a view should be destroyed.
- *
- * See https://reactnative.dev/docs/appregistry.html#unmountapplicationcomponentatroottag
- */
- unmountApplicationComponentAtRootTag(rootTag: number): void {
- ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
- },
- /**
- * Register a headless task. A headless task is a bit of code that runs without a UI.
- *
- * See https://reactnative.dev/docs/appregistry.html#registerheadlesstask
- */
- registerHeadlessTask(taskKey: string, taskProvider: TaskProvider): void {
- this.registerCancellableHeadlessTask(taskKey, taskProvider, () => () => {
- /* Cancel is no-op */
- });
- },
- /**
- * Register a cancellable headless task. A headless task is a bit of code that runs without a UI.
- *
- * See https://reactnative.dev/docs/appregistry.html#registercancellableheadlesstask
- */
- registerCancellableHeadlessTask(
- taskKey: string,
- taskProvider: TaskProvider,
- taskCancelProvider: TaskCancelProvider,
- ): void {
- if (taskProviders.has(taskKey)) {
- console.warn(
- `registerHeadlessTask or registerCancellableHeadlessTask called multiple times for same key '${taskKey}'`,
- );
- }
- taskProviders.set(taskKey, taskProvider);
- taskCancelProviders.set(taskKey, taskCancelProvider);
- },
- /**
- * Only called from native code. Starts a headless task.
- *
- * See https://reactnative.dev/docs/appregistry.html#startheadlesstask
- */
- startHeadlessTask(taskId: number, taskKey: string, data: any): void {
- const taskProvider = taskProviders.get(taskKey);
- if (!taskProvider) {
- console.warn(`No task registered for key ${taskKey}`);
- if (NativeHeadlessJsTaskSupport) {
- NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
- }
- return;
- }
- taskProvider()(data)
- .then(() => {
- if (NativeHeadlessJsTaskSupport) {
- NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
- }
- })
- .catch(reason => {
- console.error(reason);
- if (
- NativeHeadlessJsTaskSupport &&
- reason instanceof HeadlessJsTaskError
- ) {
- NativeHeadlessJsTaskSupport.notifyTaskRetry(taskId).then(
- retryPosted => {
- if (!retryPosted) {
- NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
- }
- },
- );
- }
- });
- },
- /**
- * Only called from native code. Cancels a headless task.
- *
- * See https://reactnative.dev/docs/appregistry.html#cancelheadlesstask
- */
- cancelHeadlessTask(taskId: number, taskKey: string): void {
- const taskCancelProvider = taskCancelProviders.get(taskKey);
- if (!taskCancelProvider) {
- throw new Error(`No task canceller registered for key '${taskKey}'`);
- }
- taskCancelProvider()();
- },
- };
- BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);
- if (__DEV__) {
- const LogBoxInspector = require('../LogBox/LogBoxInspectorContainer').default;
- AppRegistry.registerComponent('LogBox', () => LogBoxInspector);
- } else {
- AppRegistry.registerComponent(
- 'LogBox',
- () =>
- function NoOp() {
- return null;
- },
- );
- }
- module.exports = AppRegistry;
|