feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,256 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
*/
import type {HandleFunction, Server} from 'connect';
import type {CacheStore, MetroCache} from 'metro-cache';
import type {CustomResolver} from 'metro-resolver';
import type {JsTransformerConfig} from 'metro-transform-worker';
import type {
DeltaResult,
Module,
ReadOnlyGraph,
SerializerOptions,
TransformResult,
} from 'metro/src/DeltaBundler/types';
import type {Reporter} from 'metro/src/lib/reporting';
import type MetroServer from 'metro/src/Server';
export interface ExtraTransformOptions {
readonly preloadedModules: {[path: string]: true} | false;
readonly ramGroups: string[];
readonly transform: Readonly<{
experimentalImportSupport: boolean;
inlineRequires: {blockList: {[path: string]: true}} | boolean;
nonInlinedRequires?: ReadonlyArray<string>;
unstable_disableES6Transforms?: boolean;
}>;
}
export interface GetTransformOptionsOpts {
dev: boolean;
hot: boolean;
platform?: string;
}
export type GetTransformOptions = (
entryPoints: ReadonlyArray<string>,
options: GetTransformOptionsOpts,
getDependenciesOf: (filePath: string) => Promise<string[]>,
) => Promise<Partial<ExtraTransformOptions>>;
export type Middleware = HandleFunction;
export type PerfAnnotations = Partial<{
string: {[key: string]: string};
int: {[key: string]: number};
double: {[key: string]: number};
bool: {[key: string]: boolean};
string_array: {[key: string]: string[]};
int_array: {[key: string]: number[]};
double_array: {[key: string]: number[]};
bool_array: {[key: string]: boolean[]};
}>;
export type PerfLoggerPointOptions = Readonly<{
/**
* The time this event point occurred, if it differs from the time the point was logged.
*/
timestamp?: number;
}>;
export interface PerfLogger {
point(name: string, opts?: PerfLoggerPointOptions): void;
annotate(annotations: PerfAnnotations): void;
subSpan(label: string): PerfLogger;
}
export interface RootPerfLogger extends PerfLogger {
start(opts?: PerfLoggerPointOptions): void;
end(
status: 'SUCCESS' | 'FAIL' | 'CANCEL',
opts?: PerfLoggerPointOptions,
): void;
}
export type PerfLoggerFactoryOptions = Readonly<{
key?: number;
}>;
export type PerfLoggerFactory = (
type: 'START_UP' | 'BUNDLING_REQUEST' | 'HMR',
opts?: PerfLoggerFactoryOptions,
) => RootPerfLogger;
export interface ResolverConfigT {
assetExts: ReadonlyArray<string>;
assetResolutions: ReadonlyArray<string>;
blacklistRE?: RegExp | RegExp[];
blockList: RegExp | RegExp[];
dependencyExtractor?: string;
disableHierarchicalLookup: boolean;
extraNodeModules: {[name: string]: string};
emptyModulePath: string;
enableGlobalPackages: boolean;
hasteImplModulePath?: string;
nodeModulesPaths: ReadonlyArray<string>;
platforms: ReadonlyArray<string>;
resolveRequest?: CustomResolver;
resolverMainFields: ReadonlyArray<string>;
sourceExts: ReadonlyArray<string>;
unstable_enableSymlinks: boolean;
unstable_conditionNames: ReadonlyArray<string>;
unstable_conditionsByPlatform: Readonly<{
[platform: string]: ReadonlyArray<string>;
}>;
unstable_enablePackageExports: boolean;
useWatchman: boolean;
requireCycleIgnorePatterns: ReadonlyArray<RegExp>;
}
export interface SerializerConfigT {
createModuleIdFactory: () => (path: string) => number;
customSerializer:
| ((
entryPoint: string,
preModules: ReadonlyArray<Module>,
graph: ReadOnlyGraph,
options: SerializerOptions,
) => Promise<string | {code: string; map: string}>)
| null;
experimentalSerializerHook: (
graph: ReadOnlyGraph,
delta: DeltaResult,
) => unknown;
getModulesRunBeforeMainModule: (entryFilePath: string) => string[];
getPolyfills: (options: {platform: string | null}) => ReadonlyArray<string>;
getRunModuleStatement: (moduleId: number | string) => string;
polyfillModuleNames: ReadonlyArray<string>;
processModuleFilter: (modules: Module) => boolean;
isThirdPartyModule: (module: {readonly path: string}) => boolean;
}
export interface TransformerConfigT extends JsTransformerConfig {
getTransformOptions: GetTransformOptions;
transformVariants: Readonly<{[name: string]: unknown}>;
workerPath: string;
publicPath: string;
}
export interface MetalConfigT {
cacheStores: ReadonlyArray<CacheStore<TransformResult>>;
cacheVersion: string;
fileMapCacheDirectory?: string;
/** Deprecated, alias of fileMapCacheDirectory */
hasteMapCacheDirectory?: string;
maxWorkers: number;
unstable_perfLoggerFactory?: PerfLoggerFactory | null;
projectRoot: string;
stickyWorkers: boolean;
transformerPath: string;
reporter: Reporter;
resetCache: boolean;
watchFolders: ReadonlyArray<string>;
}
export interface ServerConfigT {
/** @deprecated */
enhanceMiddleware: (
metroMiddleware: Middleware,
metroServer: MetroServer,
) => Middleware | Server;
forwardClientLogs: boolean;
port: number;
rewriteRequestUrl: (url: string) => string;
unstable_serverRoot: string | null;
useGlobalHotkey: boolean;
verifyConnections: boolean;
}
export interface SymbolicatorConfigT {
customizeFrame: (frame: {
readonly file?: string;
readonly lineNumber?: number;
readonly column?: number;
readonly methodName?: string;
}) =>
| {readonly collapse?: boolean}
| undefined
| Promise<{readonly collapse?: boolean}>
| Promise<undefined>;
}
export interface WatcherConfigT {
additionalExts: ReadonlyArray<string>;
watchman: {
deferStates: ReadonlyArray<string>;
};
healthCheck: {
enabled: boolean;
interval: number;
timeout: number;
filePrefix: string;
};
}
export interface WatcherInputConfigT
extends Partial<Omit<WatcherConfigT, 'healthCheck'>> {
healthCheck?: Partial<WatcherConfigT['healthCheck']>;
}
export interface InputConfigT
extends Partial<Omit<MetalConfigT, 'cacheStores'>> {
readonly cacheStores?:
| ReadonlyArray<CacheStore<TransformResult>>
| ((metroCache: MetroCache) => ReadonlyArray<CacheStore<TransformResult>>);
readonly resolver?: Partial<ResolverConfigT>;
readonly server?: Partial<ServerConfigT>;
readonly serializer?: Partial<SerializerConfigT>;
readonly symbolicator?: Partial<SymbolicatorConfigT>;
readonly transformer?: Partial<TransformerConfigT>;
readonly watcher?: Partial<WatcherInputConfigT>;
}
export type MetroConfig = InputConfigT;
export interface IntermediateConfigT extends MetalConfigT {
resolver: ResolverConfigT;
server: ServerConfigT;
serializer: SerializerConfigT;
symbolicator: SymbolicatorConfigT;
transformer: TransformerConfigT;
watcher: WatcherConfigT;
}
export interface ConfigT extends Readonly<MetalConfigT> {
readonly resolver: Readonly<ResolverConfigT>;
readonly server: Readonly<ServerConfigT>;
readonly serializer: Readonly<SerializerConfigT>;
readonly symbolicator: Readonly<SymbolicatorConfigT>;
readonly transformer: Readonly<TransformerConfigT>;
readonly watcher: Readonly<WatcherConfigT>;
}
export interface YargArguments {
config?: string;
cwd?: string;
port?: string | number;
host?: string;
projectRoot?: string;
watchFolders?: string[];
assetExts?: string[];
sourceExts?: string[];
platforms?: string[];
'max-workers'?: string | number;
maxWorkers?: string | number;
transformer?: string;
'reset-cache'?: boolean;
resetCache?: boolean;
verbose?: boolean;
}

View File

@@ -0,0 +1 @@
"use strict";

View File

@@ -0,0 +1,269 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
'use strict';
import type {IntermediateStackFrame} from '../../metro/src/Server/symbolicate';
import type {HandleFunction, Server} from 'connect';
import type {CacheStore} from 'metro-cache';
import typeof MetroCache from 'metro-cache';
import type {CacheManagerFactory} from 'metro-file-map';
import type {CustomResolver} from 'metro-resolver';
import type {JsTransformerConfig} from 'metro-transform-worker';
import type {TransformResult} from 'metro/src/DeltaBundler';
import type {
DeltaResult,
Module,
ReadOnlyGraph,
SerializerOptions,
} from 'metro/src/DeltaBundler/types.flow.js';
import type {Reporter} from 'metro/src/lib/reporting';
import type MetroServer from 'metro/src/Server';
export type ExtraTransformOptions = {
+preloadedModules?: {[path: string]: true, ...} | false,
+ramGroups?: Array<string>,
+transform?: {
+experimentalImportSupport?: boolean,
+inlineRequires?: {+blockList: {[string]: true, ...}, ...} | boolean,
+nonInlinedRequires?: $ReadOnlyArray<string>,
+unstable_disableES6Transforms?: boolean,
},
...
};
export type GetTransformOptionsOpts = {
dev: boolean,
hot: boolean,
platform: ?string,
};
export type GetTransformOptions = (
entryPoints: $ReadOnlyArray<string>,
options: GetTransformOptionsOpts,
getDependenciesOf: (string) => Promise<Array<string>>,
) => Promise<Partial<ExtraTransformOptions>>;
export type Middleware = HandleFunction;
type PerfAnnotations = Partial<{
string: $ReadOnly<{[key: string]: string}>,
int: $ReadOnly<{[key: string]: number}>,
double: $ReadOnly<{[key: string]: number}>,
bool: $ReadOnly<{[key: string]: boolean}>,
string_array: $ReadOnly<{[key: string]: $ReadOnlyArray<string>}>,
int_array: $ReadOnly<{[key: string]: $ReadOnlyArray<number>}>,
double_array: $ReadOnly<{[key: string]: $ReadOnlyArray<number>}>,
bool_array: $ReadOnly<{[key: string]: $ReadOnlyArray<boolean>}>,
}>;
type PerfLoggerPointOptions = $ReadOnly<{
// The time this event point occurred, if it differs from the time the point was logged.
timestamp?: number,
}>;
export interface PerfLogger {
point(name: string, opts?: PerfLoggerPointOptions): void;
annotate(annotations: PerfAnnotations): void;
subSpan(label: string): PerfLogger;
}
export interface RootPerfLogger extends PerfLogger {
start(opts?: PerfLoggerPointOptions): void;
end(
status: 'SUCCESS' | 'FAIL' | 'CANCEL',
opts?: PerfLoggerPointOptions,
): void;
}
export type PerfLoggerFactoryOptions = $ReadOnly<{
key?: number,
}>;
export type PerfLoggerFactory = (
type: 'START_UP' | 'BUNDLING_REQUEST' | 'HMR',
opts?: PerfLoggerFactoryOptions,
) => RootPerfLogger;
type ResolverConfigT = {
assetExts: $ReadOnlyArray<string>,
assetResolutions: $ReadOnlyArray<string>,
blacklistRE?: RegExp | Array<RegExp>,
blockList: RegExp | Array<RegExp>,
disableHierarchicalLookup: boolean,
dependencyExtractor: ?string,
emptyModulePath: string,
enableGlobalPackages: boolean,
unstable_enableSymlinks: boolean,
extraNodeModules: {[name: string]: string, ...},
hasteImplModulePath: ?string,
nodeModulesPaths: $ReadOnlyArray<string>,
platforms: $ReadOnlyArray<string>,
resolveRequest: ?CustomResolver,
resolverMainFields: $ReadOnlyArray<string>,
sourceExts: $ReadOnlyArray<string>,
unstable_conditionNames: $ReadOnlyArray<string>,
unstable_conditionsByPlatform: $ReadOnly<{
[platform: string]: $ReadOnlyArray<string>,
}>,
unstable_enablePackageExports: boolean,
useWatchman: boolean,
requireCycleIgnorePatterns: $ReadOnlyArray<RegExp>,
};
type SerializerConfigT = {
createModuleIdFactory: () => (path: string) => number,
customSerializer: ?(
entryPoint: string,
preModules: $ReadOnlyArray<Module<>>,
graph: ReadOnlyGraph<>,
options: SerializerOptions,
) => Promise<string | {code: string, map: string}>,
experimentalSerializerHook: (
graph: ReadOnlyGraph<>,
delta: DeltaResult<>,
) => mixed,
getModulesRunBeforeMainModule: (entryFilePath: string) => Array<string>,
getPolyfills: ({platform: ?string, ...}) => $ReadOnlyArray<string>,
getRunModuleStatement: (number | string) => string,
polyfillModuleNames: $ReadOnlyArray<string>,
processModuleFilter: (modules: Module<>) => boolean,
isThirdPartyModule: (module: $ReadOnly<{path: string, ...}>) => boolean,
};
type TransformerConfigT = {
...JsTransformerConfig,
getTransformOptions: GetTransformOptions,
// TODO(moti): Remove this Meta-internal option from Metro's public config
transformVariants: {+[name: string]: {...}},
workerPath: string,
publicPath: string,
unstable_workerThreads: boolean,
};
type MetalConfigT = {
cacheStores: $ReadOnlyArray<CacheStore<TransformResult<>>>,
cacheVersion: string,
fileMapCacheDirectory?: string,
hasteMapCacheDirectory?: string, // Deprecated, alias of fileMapCacheDirectory
unstable_fileMapCacheManagerFactory?: CacheManagerFactory,
maxWorkers: number,
unstable_perfLoggerFactory?: ?PerfLoggerFactory,
projectRoot: string,
stickyWorkers: boolean,
transformerPath: string,
reporter: Reporter,
resetCache: boolean,
watchFolders: $ReadOnlyArray<string>,
};
type ServerConfigT = {
/** @deprecated */
enhanceMiddleware: (Middleware, MetroServer) => Middleware | Server,
forwardClientLogs: boolean,
port: number,
rewriteRequestUrl: string => string,
unstable_serverRoot: ?string,
useGlobalHotkey: boolean,
verifyConnections: boolean,
};
type SymbolicatorConfigT = {
customizeFrame: ({
+file: ?string,
+lineNumber: ?number,
+column: ?number,
+methodName: ?string,
...
}) => ?{+collapse?: boolean} | Promise<?{+collapse?: boolean}>,
customizeStack: (
Array<IntermediateStackFrame>,
mixed,
) => Array<IntermediateStackFrame> | Promise<Array<IntermediateStackFrame>>,
};
type WatcherConfigT = {
additionalExts: $ReadOnlyArray<string>,
healthCheck: {
enabled: boolean,
interval: number,
timeout: number,
filePrefix: string,
},
unstable_workerThreads: boolean,
watchman: {
deferStates: $ReadOnlyArray<string>,
},
};
export type InputConfigT = Partial<{
...MetalConfigT,
...$ReadOnly<{
cacheStores:
| $ReadOnlyArray<CacheStore<TransformResult<>>>
| (MetroCache => $ReadOnlyArray<CacheStore<TransformResult<>>>),
resolver: $ReadOnly<Partial<ResolverConfigT>>,
server: $ReadOnly<Partial<ServerConfigT>>,
serializer: $ReadOnly<Partial<SerializerConfigT>>,
symbolicator: $ReadOnly<Partial<SymbolicatorConfigT>>,
transformer: $ReadOnly<Partial<TransformerConfigT>>,
watcher: $ReadOnly<
Partial<{
...WatcherConfigT,
healthCheck?: $ReadOnly<Partial<WatcherConfigT['healthCheck']>>,
}>,
>,
}>,
}>;
export type MetroConfig = InputConfigT;
export type IntermediateConfigT = {
...MetalConfigT,
...{
resolver: ResolverConfigT,
server: ServerConfigT,
serializer: SerializerConfigT,
symbolicator: SymbolicatorConfigT,
transformer: TransformerConfigT,
watcher: WatcherConfigT,
},
};
export type ConfigT = $ReadOnly<{
...$ReadOnly<MetalConfigT>,
...$ReadOnly<{
resolver: $ReadOnly<ResolverConfigT>,
server: $ReadOnly<ServerConfigT>,
serializer: $ReadOnly<SerializerConfigT>,
symbolicator: $ReadOnly<SymbolicatorConfigT>,
transformer: $ReadOnly<TransformerConfigT>,
watcher: $ReadOnly<WatcherConfigT>,
}>,
}>;
export type YargArguments = $ReadOnly<{
config?: string,
cwd?: string,
port?: string | number,
host?: string,
projectRoot?: string,
watchFolders?: Array<string>,
assetExts?: Array<string>,
sourceExts?: Array<string>,
platforms?: Array<string>,
'max-workers'?: string | number,
maxWorkers?: string | number,
transformer?: string,
'reset-cache'?: boolean,
resetCache?: boolean,
verbose?: boolean,
...
}>;

View File

@@ -0,0 +1,53 @@
"use strict";
const defaultCreateModuleIdFactory = require("metro/src/lib/createModuleIdFactory");
exports.assetExts = [
"bmp",
"gif",
"jpg",
"jpeg",
"png",
"psd",
"svg",
"webp",
"m4v",
"mov",
"mp4",
"mpeg",
"mpg",
"webm",
"aac",
"aiff",
"caf",
"m4a",
"mp3",
"wav",
"html",
"pdf",
"yaml",
"yml",
"otf",
"ttf",
"zip",
];
exports.assetResolutions = ["1", "1.5", "2", "3", "4"];
exports.sourceExts = ["js", "jsx", "json", "ts", "tsx"];
exports.additionalExts = ["cjs", "mjs"];
exports.moduleSystem = require.resolve(
"metro-runtime/src/polyfills/require.js"
);
exports.platforms = ["ios", "android", "windows", "web"];
exports.DEFAULT_METRO_MINIFIER_PATH = "metro-minify-terser";
exports.defaultCreateModuleIdFactory = defaultCreateModuleIdFactory;
exports.noopPerfLoggerFactory = () => {
class Logger {
start() {}
end() {}
annotate() {}
point() {}
subSpan() {
return this;
}
}
return new Logger();
};

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
'use strict';
import type {PerfLogger, RootPerfLogger} from '../configTypes.flow';
const defaultCreateModuleIdFactory = require('metro/src/lib/createModuleIdFactory');
exports.assetExts = [
// Image formats
'bmp',
'gif',
'jpg',
'jpeg',
'png',
'psd',
'svg',
'webp',
// Video formats
'm4v',
'mov',
'mp4',
'mpeg',
'mpg',
'webm',
// Audio formats
'aac',
'aiff',
'caf',
'm4a',
'mp3',
'wav',
// Document formats
'html',
'pdf',
'yaml',
'yml',
// Font formats
'otf',
'ttf',
// Archives (virtual files)
'zip',
];
exports.assetResolutions = ['1', '1.5', '2', '3', '4'];
exports.sourceExts = ['js', 'jsx', 'json', 'ts', 'tsx'];
exports.additionalExts = ['cjs', 'mjs'];
exports.moduleSystem = (require.resolve(
'metro-runtime/src/polyfills/require.js',
): string);
exports.platforms = ['ios', 'android', 'windows', 'web'];
exports.DEFAULT_METRO_MINIFIER_PATH = 'metro-minify-terser';
exports.defaultCreateModuleIdFactory = defaultCreateModuleIdFactory;
exports.noopPerfLoggerFactory = (): RootPerfLogger => {
class Logger {
start() {}
end() {}
annotate() {}
point() {}
subSpan(): PerfLogger {
return this;
}
}
return new Logger();
};

View File

@@ -0,0 +1,22 @@
"use strict";
var path = require("path");
var list = [/\/__tests__\/.*/];
function escapeRegExp(pattern) {
if (Object.prototype.toString.call(pattern) === "[object RegExp]") {
return pattern.source.replace(/\/|\\\//g, "\\" + path.sep);
} else if (typeof pattern === "string") {
var escaped = pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
return escaped.replaceAll("/", "\\" + path.sep);
} else {
throw new Error("Unexpected exclusion pattern: " + pattern);
}
}
function exclusionList(additionalExclusions) {
return new RegExp(
"(" +
(additionalExclusions || []).concat(list).map(escapeRegExp).join("|") +
")$"
);
}
module.exports = exclusionList;

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
*/
import type {ConfigT} from '../configTypes';
export default interface getDefaultConfig {
(rootPath: string | null): Promise<ConfigT>;
getDefaultValues: (rootPath: string | null) => ConfigT;
}

View File

@@ -0,0 +1,156 @@
"use strict";
const {
DEFAULT_METRO_MINIFIER_PATH,
additionalExts,
assetExts,
assetResolutions,
defaultCreateModuleIdFactory,
noopPerfLoggerFactory,
platforms,
sourceExts,
} = require("./defaults");
const exclusionList = require("./exclusionList");
const { FileStore } = require("metro-cache");
const { Terminal } = require("metro-core");
const getMaxWorkers = require("metro/src/lib/getMaxWorkers");
const TerminalReporter = require("metro/src/lib/TerminalReporter");
const os = require("os");
const path = require("path");
const getDefaultValues = (projectRoot) => ({
resolver: {
assetExts,
assetResolutions,
platforms,
sourceExts,
blockList: exclusionList(),
dependencyExtractor: undefined,
disableHierarchicalLookup: false,
unstable_enableSymlinks: true,
emptyModulePath: require.resolve(
"metro-runtime/src/modules/empty-module.js"
),
enableGlobalPackages: false,
extraNodeModules: {},
hasteImplModulePath: undefined,
nodeModulesPaths: [],
resolveRequest: null,
resolverMainFields: ["browser", "main"],
unstable_conditionNames: ["require", "import"],
unstable_conditionsByPlatform: {
web: ["browser"],
},
unstable_enablePackageExports: false,
useWatchman: true,
requireCycleIgnorePatterns: [/(^|\/|\\)node_modules($|\/|\\)/],
},
serializer: {
polyfillModuleNames: [],
getRunModuleStatement: (moduleId) => `__r(${JSON.stringify(moduleId)});`,
getPolyfills: () => [],
getModulesRunBeforeMainModule: () => [],
processModuleFilter: (module) => true,
createModuleIdFactory: defaultCreateModuleIdFactory,
experimentalSerializerHook: () => {},
customSerializer: null,
isThirdPartyModule: (module) =>
/(?:^|[/\\])node_modules[/\\]/.test(module.path),
},
server: {
enhanceMiddleware: (middleware, _) => middleware,
forwardClientLogs: true,
port: 8081,
rewriteRequestUrl: (url) => url,
unstable_serverRoot: null,
useGlobalHotkey: true,
verifyConnections: false,
},
symbolicator: {
customizeFrame: () => {},
customizeStack: async (stack, _) => stack,
},
transformer: {
assetPlugins: [],
asyncRequireModulePath: "metro-runtime/src/modules/asyncRequire",
assetRegistryPath: "missing-asset-registry-path",
babelTransformerPath: "metro-babel-transformer",
dynamicDepsInPackages: "throwAtRuntime",
enableBabelRCLookup: true,
enableBabelRuntime: true,
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
unstable_disableES6Transforms: false,
},
preloadedModules: false,
ramGroups: [],
}),
globalPrefix: "",
hermesParser: false,
minifierConfig: {
mangle: {
toplevel: false,
},
output: {
ascii_only: true,
quote_style: 3,
wrap_iife: true,
},
sourceMap: {
includeSources: false,
},
toplevel: false,
compress: {
reduce_funcs: false,
},
},
minifierPath: DEFAULT_METRO_MINIFIER_PATH,
optimizationSizeLimit: 150 * 1024,
transformVariants: {
default: {},
},
workerPath: "metro/src/DeltaBundler/Worker",
publicPath: "/assets",
allowOptionalDependencies: false,
unstable_allowRequireContext: false,
unstable_dependencyMapReservedName: null,
unstable_disableModuleWrapping: false,
unstable_disableNormalizePseudoGlobals: false,
unstable_renameRequire: true,
unstable_compactOutput: false,
unstable_workerThreads: false,
},
watcher: {
additionalExts,
healthCheck: {
enabled: false,
filePrefix: ".metro-health-check",
interval: 30000,
timeout: 5000,
},
unstable_workerThreads: false,
watchman: {
deferStates: ["hg.update"],
},
},
cacheStores: [
new FileStore({
root: path.join(os.tmpdir(), "metro-cache"),
}),
],
cacheVersion: "1.0",
projectRoot: projectRoot || path.resolve(__dirname, "../../.."),
stickyWorkers: true,
watchFolders: [],
transformerPath: "metro-transform-worker",
maxWorkers: getMaxWorkers(),
resetCache: false,
reporter: new TerminalReporter(new Terminal(process.stdout)),
unstable_perfLoggerFactory: noopPerfLoggerFactory,
});
async function getDefaultConfig(rootPath) {
return getDefaultValues(rootPath);
}
getDefaultConfig.getDefaultValues = getDefaultValues;
module.exports = getDefaultConfig;

View File

@@ -0,0 +1,181 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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
* @oncall react_native
*/
'use strict';
import type {ConfigT} from '../configTypes.flow';
const {
DEFAULT_METRO_MINIFIER_PATH,
additionalExts,
assetExts,
assetResolutions,
defaultCreateModuleIdFactory,
noopPerfLoggerFactory,
platforms,
sourceExts,
} = require('./defaults');
const exclusionList = require('./exclusionList');
const {FileStore} = require('metro-cache');
const {Terminal} = require('metro-core');
const getMaxWorkers = require('metro/src/lib/getMaxWorkers');
const TerminalReporter = require('metro/src/lib/TerminalReporter');
const os = require('os');
const path = require('path');
const getDefaultValues = (projectRoot: ?string): ConfigT => ({
resolver: {
assetExts,
assetResolutions,
platforms,
sourceExts,
blockList: exclusionList(),
dependencyExtractor: undefined,
disableHierarchicalLookup: false,
unstable_enableSymlinks: true,
emptyModulePath: require.resolve(
'metro-runtime/src/modules/empty-module.js',
),
enableGlobalPackages: false,
extraNodeModules: {},
hasteImplModulePath: undefined,
nodeModulesPaths: [],
resolveRequest: null,
resolverMainFields: ['browser', 'main'],
unstable_conditionNames: ['require', 'import'],
unstable_conditionsByPlatform: {
web: ['browser'],
},
unstable_enablePackageExports: false,
useWatchman: true,
requireCycleIgnorePatterns: [/(^|\/|\\)node_modules($|\/|\\)/],
},
serializer: {
polyfillModuleNames: [],
getRunModuleStatement: (moduleId: number | string) =>
`__r(${JSON.stringify(moduleId)});`,
getPolyfills: () => [],
getModulesRunBeforeMainModule: () => [],
processModuleFilter: module => true,
createModuleIdFactory: defaultCreateModuleIdFactory,
experimentalSerializerHook: () => {},
customSerializer: null,
isThirdPartyModule: module =>
/(?:^|[/\\])node_modules[/\\]/.test(module.path),
},
server: {
enhanceMiddleware: (middleware, _) => middleware,
forwardClientLogs: true,
port: 8081,
rewriteRequestUrl: url => url,
unstable_serverRoot: null,
useGlobalHotkey: true,
verifyConnections: false,
},
symbolicator: {
customizeFrame: () => {},
customizeStack: async (stack, _) => stack,
},
transformer: {
assetPlugins: [],
asyncRequireModulePath: 'metro-runtime/src/modules/asyncRequire',
assetRegistryPath: 'missing-asset-registry-path',
babelTransformerPath: 'metro-babel-transformer',
dynamicDepsInPackages: 'throwAtRuntime',
enableBabelRCLookup: true,
enableBabelRuntime: true,
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
unstable_disableES6Transforms: false,
},
preloadedModules: false,
ramGroups: [],
}),
globalPrefix: '',
hermesParser: false,
minifierConfig: {
mangle: {
toplevel: false,
},
output: {
ascii_only: true,
quote_style: 3,
wrap_iife: true,
},
sourceMap: {
includeSources: false,
},
toplevel: false,
compress: {
// reduce_funcs inlines single-use functions, which cause perf regressions.
reduce_funcs: false,
},
},
minifierPath: DEFAULT_METRO_MINIFIER_PATH,
optimizationSizeLimit: 150 * 1024, // 150 KiB.
transformVariants: {default: {}},
workerPath: 'metro/src/DeltaBundler/Worker',
publicPath: '/assets',
allowOptionalDependencies: false,
unstable_allowRequireContext: false,
unstable_dependencyMapReservedName: null,
unstable_disableModuleWrapping: false,
unstable_disableNormalizePseudoGlobals: false,
unstable_renameRequire: true,
unstable_compactOutput: false,
unstable_workerThreads: false,
},
watcher: {
additionalExts,
healthCheck: {
enabled: false,
filePrefix: '.metro-health-check',
interval: 30000,
timeout: 5000,
},
unstable_workerThreads: false,
watchman: {
deferStates: ['hg.update'],
},
},
cacheStores: [
new FileStore({
root: path.join(os.tmpdir(), 'metro-cache'),
}),
],
cacheVersion: '1.0',
// We assume the default project path is two levels up from
// node_modules/metro/
projectRoot: projectRoot || path.resolve(__dirname, '../../..'),
stickyWorkers: true,
watchFolders: [],
transformerPath: 'metro-transform-worker',
maxWorkers: getMaxWorkers(),
resetCache: false,
reporter: new TerminalReporter(new Terminal(process.stdout)),
unstable_perfLoggerFactory: noopPerfLoggerFactory,
});
async function getDefaultConfig(rootPath: ?string): Promise<ConfigT> {
// We can add more logic here to get a sensible default configuration, for
// now we just return a stub.
return getDefaultValues(rootPath);
}
getDefaultConfig.getDefaultValues = getDefaultValues;
module.exports = getDefaultConfig;

View File

@@ -0,0 +1,26 @@
"use strict";
module.exports = async () => {
const defaultConfig = await require("./index")("/path/to/project");
const validConfig = {
...defaultConfig,
resolver: {
...defaultConfig.resolver,
resolveRequest: function CustomResolver() {},
hasteImplModulePath: "./path",
},
server: {
...defaultConfig.server,
unstable_serverRoot: "",
},
transformer: {
...defaultConfig.transformer,
getTransformOptions: function getTransformOptions() {},
},
serializer: {
...defaultConfig.serializer,
customSerializer: function customSerializer() {},
},
};
return validConfig;
};

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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
* @oncall react_native
*/
'use strict';
module.exports = (async () => {
const defaultConfig = await require('./index')('/path/to/project');
const validConfig = {
...defaultConfig,
resolver: {
...defaultConfig.resolver,
resolveRequest: function CustomResolver() {},
hasteImplModulePath: './path',
},
server: {
...defaultConfig.server,
unstable_serverRoot: '',
},
transformer: {
...defaultConfig.transformer,
getTransformOptions: function getTransformOptions() {},
},
serializer: {
...defaultConfig.serializer,
customSerializer: function customSerializer() {},
},
};
return validConfig;
}: () => any);

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
*/
import getDefaultConfig from './defaults';
import {loadConfig, mergeConfig, resolveConfig} from './loadConfig';
export * from './configTypes';
export {loadConfig, mergeConfig, resolveConfig, getDefaultConfig};

View File

@@ -0,0 +1,13 @@
"use strict";
try {
require("metro-babel-register").unstable_registerForMetroMonorepo();
} catch {}
const getDefaultConfig = require("./defaults");
const { loadConfig, mergeConfig, resolveConfig } = require("./loadConfig");
module.exports = {
loadConfig,
resolveConfig,
mergeConfig,
getDefaultConfig,
};

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
'use strict';
/*::
export type * from './configTypes.flow';
*/
try {
require('metro-babel-register').unstable_registerForMetroMonorepo();
} catch {}
const getDefaultConfig = require('./defaults');
const {loadConfig, mergeConfig, resolveConfig} = require('./loadConfig');
module.exports = {
loadConfig,
resolveConfig,
mergeConfig,
getDefaultConfig,
};

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
*/
import type {ConfigT, InputConfigT, YargArguments} from './configTypes';
export interface CosmiConfigResult {
filepath: string;
isEmpty: boolean;
config:
| ((partialConfig: ConfigT) => Promise<ConfigT>)
| ((partialConfig: ConfigT) => ConfigT)
| InputConfigT;
}
export function loadConfig(
argv?: YargArguments,
defaultConfigOverrides?: InputConfigT,
): Promise<ConfigT>;
export function resolveConfig(
filePath?: string,
cwd?: string,
): Promise<CosmiConfigResult>;
export function mergeConfig(
defaultConfig: InputConfigT,
...configs: InputConfigT[]
): ConfigT;

View File

@@ -0,0 +1,211 @@
"use strict";
const getDefaultConfig = require("./defaults");
const validConfig = require("./defaults/validConfig");
const cosmiconfig = require("cosmiconfig");
const fs = require("fs");
const { validate } = require("jest-validate");
const MetroCache = require("metro-cache");
const path = require("path");
const { dirname, join } = require("path");
function overrideArgument(arg) {
if (arg == null) {
return arg;
}
if (Array.isArray(arg)) {
return arg[arg.length - 1];
}
return arg;
}
const explorer = cosmiconfig("metro", {
searchPlaces: [
"metro.config.js",
"metro.config.cjs",
"metro.config.json",
"package.json",
],
loaders: {
".json": cosmiconfig.loadJson,
".yaml": cosmiconfig.loadYaml,
".yml": cosmiconfig.loadYaml,
".js": cosmiconfig.loadJs,
".cjs": cosmiconfig.loadJs,
".es6": cosmiconfig.loadJs,
noExt: cosmiconfig.loadYaml,
},
});
const isFile = (filePath) =>
fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory();
const resolve = (filePath) => {
try {
return require.resolve(filePath);
} catch (error) {
if (path.isAbsolute(filePath) || error.code !== "MODULE_NOT_FOUND") {
throw error;
}
}
const possiblePath = path.resolve(process.cwd(), filePath);
return isFile(possiblePath) ? possiblePath : filePath;
};
async function resolveConfig(filePath, cwd) {
if (filePath) {
return explorer.load(resolve(filePath));
}
const result = await explorer.search(cwd);
if (result == null) {
return {
isEmpty: true,
filepath: join(cwd || process.cwd(), "metro.config.stub.js"),
config: {},
};
}
return result;
}
function mergeConfig(defaultConfig, ...configs) {
return configs.reduce(
(totalConfig, nextConfig) => ({
...totalConfig,
...nextConfig,
cacheStores:
nextConfig.cacheStores != null
? typeof nextConfig.cacheStores === "function"
? nextConfig.cacheStores(MetroCache)
: nextConfig.cacheStores
: totalConfig.cacheStores,
resolver: {
...totalConfig.resolver,
...(nextConfig.resolver || {}),
dependencyExtractor:
nextConfig.resolver && nextConfig.resolver.dependencyExtractor != null
? resolve(nextConfig.resolver.dependencyExtractor)
: totalConfig.resolver.dependencyExtractor,
hasteImplModulePath:
nextConfig.resolver && nextConfig.resolver.hasteImplModulePath != null
? resolve(nextConfig.resolver.hasteImplModulePath)
: totalConfig.resolver.hasteImplModulePath,
},
serializer: {
...totalConfig.serializer,
...(nextConfig.serializer || {}),
},
transformer: {
...totalConfig.transformer,
...(nextConfig.transformer || {}),
babelTransformerPath:
nextConfig.transformer &&
nextConfig.transformer.babelTransformerPath != null
? resolve(nextConfig.transformer.babelTransformerPath)
: totalConfig.transformer.babelTransformerPath,
},
server: {
...totalConfig.server,
...(nextConfig.server || {}),
},
symbolicator: {
...totalConfig.symbolicator,
...(nextConfig.symbolicator || {}),
},
watcher: {
...totalConfig.watcher,
...nextConfig.watcher,
watchman: {
...totalConfig.watcher?.watchman,
...nextConfig.watcher?.watchman,
},
healthCheck: {
...totalConfig.watcher?.healthCheck,
...nextConfig.watcher?.healthCheck,
},
},
}),
defaultConfig
);
}
async function loadMetroConfigFromDisk(path, cwd, defaultConfigOverrides) {
const resolvedConfigResults = await resolveConfig(path, cwd);
const { config: configModule, filepath } = resolvedConfigResults;
const rootPath = dirname(filepath);
const defaults = await getDefaultConfig(rootPath);
const defaultConfig = mergeConfig(defaults, defaultConfigOverrides);
if (typeof configModule === "function") {
const resultedConfig = await configModule(defaultConfig);
return mergeConfig(defaultConfig, resultedConfig);
}
return mergeConfig(defaultConfig, configModule);
}
function overrideConfigWithArguments(config, argv) {
const output = {
resolver: {},
serializer: {},
server: {},
transformer: {},
};
if (argv.port != null) {
output.server.port = Number(argv.port);
}
if (argv.projectRoot != null) {
output.projectRoot = argv.projectRoot;
}
if (argv.watchFolders != null) {
output.watchFolders = argv.watchFolders;
}
if (argv.assetExts != null) {
output.resolver.assetExts = argv.assetExts;
}
if (argv.sourceExts != null) {
output.resolver.sourceExts = argv.sourceExts;
}
if (argv.platforms != null) {
output.resolver.platforms = argv.platforms;
}
if (argv["max-workers"] != null || argv.maxWorkers != null) {
output.maxWorkers = Number(argv["max-workers"] || argv.maxWorkers);
}
if (argv.transformer != null) {
output.transformer.babelTransformerPath = argv.transformer;
}
if (argv["reset-cache"] != null) {
output.resetCache = argv["reset-cache"];
}
if (argv.resetCache != null) {
output.resetCache = argv.resetCache;
}
if (argv.verbose === false) {
output.reporter = {
update: () => {},
};
}
return mergeConfig(config, output);
}
async function loadConfig(argvInput = {}, defaultConfigOverrides = {}) {
const argv = {
...argvInput,
config: overrideArgument(argvInput.config),
};
const configuration = await loadMetroConfigFromDisk(
argv.config,
argv.cwd,
defaultConfigOverrides
);
validate(configuration, {
exampleConfig: await validConfig(),
recursiveDenylist: ["reporter", "resolver", "transformer"],
deprecatedConfig: {
blacklistRE:
() => `Warning: Metro config option \`blacklistRE\` is deprecated.
Please use \`blockList\` instead.`,
},
});
const configWithArgs = overrideConfigWithArguments(configuration, argv);
const overriddenConfig = {};
overriddenConfig.watchFolders = [
configWithArgs.projectRoot,
...configWithArgs.watchFolders,
];
return mergeConfig(configWithArgs, overriddenConfig);
}
module.exports = {
loadConfig,
resolveConfig,
mergeConfig,
};

View File

@@ -0,0 +1,341 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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
* @oncall react_native
*/
'use strict';
import type {ConfigT, InputConfigT, YargArguments} from './configTypes.flow';
const getDefaultConfig = require('./defaults');
const validConfig = require('./defaults/validConfig');
const cosmiconfig = require('cosmiconfig');
const fs = require('fs');
const {validate} = require('jest-validate');
const MetroCache = require('metro-cache');
const path = require('path');
const {dirname, join} = require('path');
type CosmiConfigResult = {
filepath: string,
isEmpty: boolean,
config: (ConfigT => Promise<ConfigT>) | (ConfigT => ConfigT) | InputConfigT,
...
};
/**
* Takes the last argument if multiple of the same argument are given
*/
function overrideArgument<T>(arg: Array<T> | T): T {
if (arg == null) {
return arg;
}
if (Array.isArray(arg)) {
// $FlowFixMe[incompatible-return]
return arg[arg.length - 1];
}
return arg;
}
const explorer = cosmiconfig('metro', {
searchPlaces: [
'metro.config.js',
'metro.config.cjs',
'metro.config.json',
'package.json',
],
loaders: {
'.json': cosmiconfig.loadJson,
'.yaml': cosmiconfig.loadYaml,
'.yml': cosmiconfig.loadYaml,
'.js': cosmiconfig.loadJs,
'.cjs': cosmiconfig.loadJs,
'.es6': cosmiconfig.loadJs,
noExt: cosmiconfig.loadYaml,
},
});
const isFile = (filePath: string) =>
fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory();
const resolve = (filePath: string) => {
// Attempt to resolve the path with the node resolution algorithm but fall back to resolving
// the file relative to the current working directory if the input is not an absolute path.
try {
return require.resolve(filePath);
} catch (error) {
if (path.isAbsolute(filePath) || error.code !== 'MODULE_NOT_FOUND') {
throw error;
}
}
const possiblePath = path.resolve(process.cwd(), filePath);
return isFile(possiblePath) ? possiblePath : filePath;
};
async function resolveConfig(
filePath?: string,
cwd?: string,
): Promise<CosmiConfigResult> {
if (filePath) {
return explorer.load(resolve(filePath));
}
const result = await explorer.search(cwd);
if (result == null) {
// No config file found, return a default
return {
isEmpty: true,
filepath: join(cwd || process.cwd(), 'metro.config.stub.js'),
config: {},
};
}
return result;
}
function mergeConfig<T: $ReadOnly<InputConfigT>>(
defaultConfig: T,
...configs: Array<InputConfigT>
): T {
// If the file is a plain object we merge the file with the default config,
// for the function we don't do this since that's the responsibility of the user
return configs.reduce(
(totalConfig, nextConfig) => ({
...totalConfig,
...nextConfig,
cacheStores:
nextConfig.cacheStores != null
? typeof nextConfig.cacheStores === 'function'
? nextConfig.cacheStores(MetroCache)
: nextConfig.cacheStores
: totalConfig.cacheStores,
resolver: {
...totalConfig.resolver,
// $FlowFixMe[exponential-spread]
...(nextConfig.resolver || {}),
dependencyExtractor:
nextConfig.resolver && nextConfig.resolver.dependencyExtractor != null
? resolve(nextConfig.resolver.dependencyExtractor)
: // $FlowFixMe[incompatible-use]
totalConfig.resolver.dependencyExtractor,
hasteImplModulePath:
nextConfig.resolver && nextConfig.resolver.hasteImplModulePath != null
? resolve(nextConfig.resolver.hasteImplModulePath)
: // $FlowFixMe[incompatible-use]
totalConfig.resolver.hasteImplModulePath,
},
serializer: {
...totalConfig.serializer,
// $FlowFixMe[exponential-spread]
...(nextConfig.serializer || {}),
},
transformer: {
...totalConfig.transformer,
// $FlowFixMe[exponential-spread]
...(nextConfig.transformer || {}),
babelTransformerPath:
nextConfig.transformer &&
nextConfig.transformer.babelTransformerPath != null
? resolve(nextConfig.transformer.babelTransformerPath)
: // $FlowFixMe[incompatible-use]
totalConfig.transformer.babelTransformerPath,
},
server: {
...totalConfig.server,
// $FlowFixMe[exponential-spread]
...(nextConfig.server || {}),
},
symbolicator: {
...totalConfig.symbolicator,
// $FlowFixMe[exponential-spread]
...(nextConfig.symbolicator || {}),
},
watcher: {
...totalConfig.watcher,
// $FlowFixMe[exponential-spread]
...nextConfig.watcher,
watchman: {
// $FlowFixMe[exponential-spread]
...totalConfig.watcher?.watchman,
...nextConfig.watcher?.watchman,
},
healthCheck: {
// $FlowFixMe[exponential-spread]
...totalConfig.watcher?.healthCheck,
// $FlowFixMe: Spreading shapes creates an explosion of union types
...nextConfig.watcher?.healthCheck,
},
},
}),
defaultConfig,
);
}
async function loadMetroConfigFromDisk(
path?: string,
cwd?: string,
defaultConfigOverrides: InputConfigT,
): Promise<ConfigT> {
const resolvedConfigResults: CosmiConfigResult = await resolveConfig(
path,
cwd,
);
const {config: configModule, filepath} = resolvedConfigResults;
const rootPath = dirname(filepath);
const defaults = await getDefaultConfig(rootPath);
// $FlowFixMe[incompatible-variance]
// $FlowFixMe[incompatible-call]
const defaultConfig: ConfigT = mergeConfig(defaults, defaultConfigOverrides);
if (typeof configModule === 'function') {
// Get a default configuration based on what we know, which we in turn can pass
// to the function.
const resultedConfig = await configModule(defaultConfig);
// $FlowFixMe[incompatible-call]
// $FlowFixMe[incompatible-variance]
return mergeConfig(defaultConfig, resultedConfig);
}
// $FlowFixMe[incompatible-variance]
// $FlowFixMe[incompatible-call]
return mergeConfig(defaultConfig, configModule);
}
function overrideConfigWithArguments(
config: ConfigT,
argv: YargArguments,
): ConfigT {
// We override some config arguments here with the argv
const output: InputConfigT = {
resolver: {},
serializer: {},
server: {},
transformer: {},
};
if (argv.port != null) {
// $FlowFixMe[incompatible-use]
// $FlowFixMe[cannot-write]
output.server.port = Number(argv.port);
}
if (argv.projectRoot != null) {
output.projectRoot = argv.projectRoot;
}
if (argv.watchFolders != null) {
output.watchFolders = argv.watchFolders;
}
if (argv.assetExts != null) {
// $FlowFixMe[incompatible-use]
// $FlowFixMe[cannot-write]
output.resolver.assetExts = argv.assetExts;
}
if (argv.sourceExts != null) {
// $FlowFixMe[incompatible-use]
// $FlowFixMe[cannot-write]
output.resolver.sourceExts = argv.sourceExts;
}
if (argv.platforms != null) {
// $FlowFixMe[incompatible-use]
// $FlowFixMe[cannot-write]
output.resolver.platforms = argv.platforms;
}
if (argv['max-workers'] != null || argv.maxWorkers != null) {
output.maxWorkers = Number(argv['max-workers'] || argv.maxWorkers);
}
if (argv.transformer != null) {
// $FlowFixMe[incompatible-use]
// $FlowFixMe[cannot-write]
output.transformer.babelTransformerPath = argv.transformer;
}
if (argv['reset-cache'] != null) {
output.resetCache = argv['reset-cache'];
}
if (argv.resetCache != null) {
output.resetCache = argv.resetCache;
}
if (argv.verbose === false) {
output.reporter = {update: () => {}};
// TODO: Ask if this is the way to go
}
// $FlowFixMe[incompatible-variance]
// $FlowFixMe[incompatible-call]
return mergeConfig(config, output);
}
/**
* Load the metro configuration from disk
* @param {object} argv Arguments coming from the CLI, can be empty
* @param {object} defaultConfigOverrides A configuration that can override the default config
* @return {object} Configuration returned
*/
async function loadConfig(
argvInput?: YargArguments = {},
defaultConfigOverrides?: InputConfigT = {},
): Promise<ConfigT> {
const argv = {...argvInput, config: overrideArgument(argvInput.config)};
const configuration = await loadMetroConfigFromDisk(
argv.config,
argv.cwd,
defaultConfigOverrides,
);
validate(configuration, {
exampleConfig: await validConfig(),
recursiveDenylist: ['reporter', 'resolver', 'transformer'],
deprecatedConfig: {
blacklistRE: () =>
`Warning: Metro config option \`blacklistRE\` is deprecated.
Please use \`blockList\` instead.`,
},
});
// Override the configuration with cli parameters
const configWithArgs = overrideConfigWithArguments(configuration, argv);
const overriddenConfig: {[string]: mixed} = {};
overriddenConfig.watchFolders = [
configWithArgs.projectRoot,
...configWithArgs.watchFolders,
];
// Set the watchfolders to include the projectRoot, as Metro assumes that is
// the case
// $FlowFixMe[incompatible-variance]
// $FlowFixMe[incompatible-indexer]
// $FlowFixMe[incompatible-call]
return mergeConfig(configWithArgs, overriddenConfig);
}
module.exports = {
loadConfig,
resolveConfig,
mergeConfig,
};