Use new configuration in react-native public cli

Summary: Change the public react-native CLI to use the new configuration of Metro.

Reviewed By: rafeca

Differential Revision: D8801217

fbshipit-source-id: 112e4812b430ebee1ed41489f803b90c182ccdb4
This commit is contained in:
Ives van Hoorne 2018-07-25 05:44:40 -07:00 committed by Facebook Github Bot
parent f0daaf3568
commit a32620dc3b
7 changed files with 80 additions and 139 deletions

View File

@ -14,15 +14,12 @@ const log = require('../util/log').out('bundle');
/* $FlowFixMe(site=react_native_oss) */
const Server = require('metro/src/Server');
const {convert} = require('metro-config');
/* $FlowFixMe(site=react_native_oss) */
const outputBundle = require('metro/src/shared/output/bundle');
const {convert} = require('metro-config');
const path = require('path');
const saveAssets = require('./saveAssets');
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
import type {RequestOptions, OutputOptions} from './types.flow';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
@ -48,6 +45,10 @@ async function buildBundle(
sourceMapUrl = path.basename(sourceMapUrl);
}
config.transformModulePath = args.transformer
? path.resolve(args.transformer)
: config.transformModulePath;
const requestOpts: RequestOptions = {
entryFile: args.entryFile,
sourceMapUrl,
@ -56,13 +57,6 @@ async function buildBundle(
platform: args.platform,
};
const transformModulePath = args.transformer
? path.resolve(args.transformer)
: config.transformModulePath;
config.transformModulePath = transformModulePath;
config.transformer.assetRegistryPath = ASSET_REGISTRY_PATH;
const {serverOptions} = convert.convertNewToOld(config);
const server = new Server(serverOptions);

View File

@ -10,7 +10,7 @@
'use strict';
const config = require('./core');
const {configPromise} = require('./core');
const assertRequiredOptions = require('./util/assertRequiredOptions');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
@ -137,7 +137,8 @@ const addCommand = (command: CommandT, cfg: RNConfig) => {
cmd.option('--config [string]', 'Path to the CLI configuration file');
};
function run() {
async function run() {
const config = await configPromise;
const setupEnvScript = /^win/.test(process.platform)
? 'setup_env.bat'
: 'setup_env.sh';

View File

@ -16,6 +16,7 @@ const findPlugins = require('./findPlugins');
const findAssets = require('./findAssets');
const ios = require('./ios');
const wrapCommands = require('./wrapCommands');
const {ASSET_REGISTRY_PATH} = require('./Constants');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
@ -28,7 +29,7 @@ const minimist = require('minimist');
const path = require('path');
import type {CommandT} from '../commands';
import type {ConfigT} from 'metro';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
export type RNConfig = {
...ConfigT,
@ -36,10 +37,6 @@ export type RNConfig = {
* Returns an object with all platform configurations.
*/
getPlatformConfig(): Object,
/**
* Returns an array of project commands used by the CLI to load
*/
getProjectCommands(): Array<CommandT>,
/**
* Returns project config from the current working directory
*/
@ -69,7 +66,7 @@ const pluginPlatforms = plugins.platforms.reduce((acc, pathToPlatforms) => {
);
}, {});
const defaultRNConfig = {
const defaultConfig = {
hasteImplModulePath: require.resolve('../../jest/hasteImpl'),
getPlatforms(): Array<string> {
@ -79,20 +76,9 @@ const defaultRNConfig = {
getProvidesModuleNodeModules(): Array<string> {
return ['react-native', 'react-native-windows'];
},
};
getProjectCommands(): Array<CommandT> {
const commands = plugins.commands.map(pathToCommands => {
const name = pathToCommands.split(path.sep)[0];
return attachPackage(
require(path.join(appRoot, 'node_modules', pathToCommands)),
require(path.join(appRoot, 'node_modules', name, 'package.json')),
);
});
return flatten(commands);
},
const defaultRNConfig = {
getPlatformConfig(): Object {
return {
ios,
@ -141,14 +127,33 @@ const defaultRNConfig = {
/**
* Loads the CLI configuration
*/
function getCliConfig(): RNConfig {
async function getCliConfig(): Promise<RNConfig> {
const cliArgs = minimist(process.argv.slice(2));
const config =
cliArgs.config != null
? Config.load(path.resolve(__dirname, cliArgs.config))
: Config.findOptional(__dirname);
const config = await Config.load(path.resolve(__dirname, cliArgs.config));
config.transformer.assetRegistryPath = ASSET_REGISTRY_PATH;
config.resolver.hasteImplModulePath = defaultConfig.hasteImplModulePath;
config.resolver.platforms = defaultConfig.getPlatforms();
config.resolver.providesModuleNodeModules = defaultConfig.getProvidesModuleNodeModules();
return {...defaultRNConfig, ...config};
}
module.exports = getCliConfig();
/**
* Returns an array of project commands used by the CLI to load
*/
function getProjectCommands(): Array<CommandT> {
const commands = plugins.commands.map(pathToCommands => {
const name = pathToCommands.split(path.sep)[0];
return attachPackage(
require(path.join(appRoot, 'node_modules', pathToCommands)),
require(path.join(appRoot, 'node_modules', name, 'package.json')),
);
});
return flatten(commands);
}
module.exports.configPromise = getCliConfig();
module.exports.getProjectCommands = getProjectCommands;

View File

@ -16,8 +16,6 @@ const denodeify = require('denodeify');
const fs = require('fs');
const path = require('path');
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
async function dependencies(argv, configPromise, args, packagerInstance) {
const rootModuleAbsolutePath = args.entryFile;
const config = await configPromise;
@ -31,12 +29,9 @@ async function dependencies(argv, configPromise, args, packagerInstance) {
config.transformModulePath = args.transformer
? path.resolve(args.transformer)
: config.transformModulePath;
config.transformer.transformModulePath = ASSET_REGISTRY_PATH;
const {serverOptions: packageOpts} = convert.convertNewToOld(config);
const relativePath = path.relative(
packageOpts.projectRoot,
config.projectRoot,
rootModuleAbsolutePath,
);
@ -53,10 +48,12 @@ async function dependencies(argv, configPromise, args, packagerInstance) {
? fs.createWriteStream(args.output)
: process.stdout;
const {serverOptions} = convert.convertNewToOld(config);
return Promise.resolve(
(packagerInstance
? packagerInstance.getOrderedDependencyPaths(options)
: Metro.getOrderedDependencyPaths(packageOpts, options)
: Metro.getOrderedDependencyPaths(serverOptions, options)
).then(deps => {
deps.forEach(modulePath => {
// Temporary hack to disable listing dependencies not under this directory.
@ -64,7 +61,7 @@ async function dependencies(argv, configPromise, args, packagerInstance) {
// (a) JS code to not depend on anything outside this directory, or
// (b) Come up with a way to declare this dependency in Buck.
const isInsideProjectRoots =
packageOpts.watchFolders.filter(root => modulePath.startsWith(root))
config.watchFolders.filter(root => modulePath.startsWith(root))
.length > 0;
if (isInsideProjectRoots) {

View File

@ -21,11 +21,8 @@ const morgan = require('morgan');
const path = require('path');
const webSocketProxy = require('./util/webSocketProxy');
const MiddlewareManager = require('./middleware/MiddlewareManager');
const {convertOldToNew} = require('metro-config/src/convertConfig');
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
import type {ConfigT} from 'metro';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
export type Args = {|
+assetExts: $ReadOnlyArray<string>,
@ -57,23 +54,14 @@ async function runServer(args: Args, config: ConfigT) {
args.watchFolders.forEach(middlewareManager.serveStatic);
const serverInstance = await Metro.runServer({
config: convertOldToNew({
config: {
...config,
assetRegistryPath: ASSET_REGISTRY_PATH,
enhanceMiddleware: middleware =>
middlewareManager.getConnectInstance().use(middleware),
transformModulePath: args.transformer
? path.resolve(args.transformer)
: config.getTransformModulePath(),
},
maxWorkers: args.maxWorkers,
port: args.port,
reporter,
}),
config.maxWorkers = args.maxWorkers;
config.server.port = args.port;
config.reporter = reporter;
config.server.enhanceMiddleware = middleware =>
middlewareManager.getConnectInstance().use(middleware);
hmrEnabled: true,
const serverInstance = await Metro.runServer({
config,
host: args.host,
secure: args.https,
secureCert: args.cert,

View File

@ -13,7 +13,7 @@
const runServer = require('./runServer');
import type {RNConfig} from '../core';
import type {ConfigT} from 'metro';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
import type {Args as RunServerArgs} from './runServer';
/**
@ -43,7 +43,7 @@ module.exports = {
command: '--projectRoot [string]',
description: 'Specify the main project root',
default: (config: ConfigT) => {
return config.getProjectRoot();
return config.projectRoot;
},
},
{
@ -52,7 +52,7 @@ module.exports = {
'Specify any additional folders to be added to the watch list',
parse: (val: string) => val.split(','),
default: (config: ConfigT) => {
return config.getWatchFolders();
return config.watchFolders;
},
},
{
@ -60,21 +60,21 @@ module.exports = {
description:
'Specify any additional asset extensions to be used by the packager',
parse: (val: string) => val.split(','),
default: (config: ConfigT) => config.getAssetExts(),
default: (config: ConfigT) => config.resolver.assetExts,
},
{
command: '--sourceExts [list]',
description:
'Specify any additional source extensions to be used by the packager',
parse: (val: string) => val.split(','),
default: (config: ConfigT) => config.getSourceExts(),
default: (config: ConfigT) => config.resolver.sourceExts,
},
{
command: '--platforms [list]',
description:
'Specify any additional platforms to be used by the packager',
parse: (val: string) => val.split(','),
default: (config: ConfigT) => config.getPlatforms(),
default: (config: ConfigT) => config.resolver.platforms,
},
{
command: '--providesModuleNodeModules [list]',
@ -82,10 +82,9 @@ module.exports = {
'Specify any npm packages that import dependencies with providesModule',
parse: (val: string) => val.split(','),
default: (config: RNConfig) => {
if (typeof config.getProvidesModuleNodeModules === 'function') {
return config.getProvidesModuleNodeModules();
}
return null;
return config.resolver
? config.resolver.providesModuleNodeModules
: undefined;
},
},
{

View File

@ -10,21 +10,16 @@
'use strict';
const findSymlinkedModules = require('./findSymlinkedModules');
const fs = require('fs');
const getPolyfills = require('../../rn-get-polyfills');
const invariant = require('fbjs/lib/invariant');
const path = require('path');
const {Config: MetroConfig, createBlacklist} = require('metro');
const RN_CLI_CONFIG = 'rn-cli.config.js';
import type {ConfigT as MetroConfigT} from 'metro';
const {createBlacklist} = require('metro');
const {loadConfig, mergeConfig} = require('metro-config');
/**
* Configuration file of the CLI.
*/
export type ConfigT = MetroConfigT;
import type {ConfigT} from 'metro-config/src/configTypes.flow';
function getProjectPath() {
if (
@ -70,68 +65,30 @@ const getBlacklistRE = () => {
* hierarchy, an error will be thrown.
*/
const Config = {
DEFAULT: ({
...MetroConfig.DEFAULT,
getBlacklistRE,
DEFAULT: {
resolver: {
resolverMainFields: ['react-native', 'browser', 'main'],
blacklistRE: getBlacklistRE(),
},
serializer: {
getModulesRunBeforeMainModule: () => [
require.resolve('../../Libraries/Core/InitializeCore'),
],
getProjectRoots,
getPolyfills,
getWatchFolders: () => [getProjectPath()],
getResolverMainFields: () => ['react-native', 'browser', 'main'],
getTransformModulePath: () =>
require.resolve('metro/src/reactNativeTransformer'),
}: ConfigT),
find(startDir: string): ConfigT {
return this.findWithPath(startDir).config;
},
findWithPath(startDir: string): {config: ConfigT, projectPath: string} {
const configPath = findConfigPath(startDir);
invariant(
configPath,
`Can't find "${RN_CLI_CONFIG}" file in any parent folder of "${startDir}"`,
);
const projectPath = path.dirname(configPath);
return {config: this.load(configPath, startDir), projectPath};
},
findOptional(startDir: string): ConfigT {
const configPath = findConfigPath(startDir);
return configPath ? this.load(configPath, startDir) : {...Config.DEFAULT};
watchFolders: [getProjectPath(), ...getProjectRoots()],
transformModulePath: require.resolve('metro/src/reactNativeTransformer'),
},
getProjectPath,
getProjectRoots,
load(configFile: string): ConfigT {
return MetroConfig.load(configFile, Config.DEFAULT);
async load(configFile: string): Promise<ConfigT> {
const config: ConfigT = await loadConfig({config: configFile});
return mergeConfig(config, this.DEFAULT);
},
};
function findConfigPath(cwd: string): ?string {
const parentDir = findParentDirectory(cwd, RN_CLI_CONFIG);
return parentDir ? path.join(parentDir, RN_CLI_CONFIG) : null;
}
// Finds the most near ancestor starting at `currentFullPath` that has
// a file named `filename`
function findParentDirectory(currentFullPath, filename) {
const root = path.parse(currentFullPath).root;
const testDir = parts => {
if (parts.length === 0) {
return null;
}
const fullPath = path.join(root, parts.join(path.sep));
var exists = fs.existsSync(path.join(fullPath, filename));
return exists ? fullPath : testDir(parts.slice(0, -1));
};
return testDir(currentFullPath.substring(root.length).split(path.sep));
}
module.exports = Config;