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:
parent
f0daaf3568
commit
a32620dc3b
|
@ -14,15 +14,12 @@ const log = require('../util/log').out('bundle');
|
||||||
/* $FlowFixMe(site=react_native_oss) */
|
/* $FlowFixMe(site=react_native_oss) */
|
||||||
const Server = require('metro/src/Server');
|
const Server = require('metro/src/Server');
|
||||||
|
|
||||||
const {convert} = require('metro-config');
|
|
||||||
|
|
||||||
/* $FlowFixMe(site=react_native_oss) */
|
/* $FlowFixMe(site=react_native_oss) */
|
||||||
const outputBundle = require('metro/src/shared/output/bundle');
|
const outputBundle = require('metro/src/shared/output/bundle');
|
||||||
|
const {convert} = require('metro-config');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const saveAssets = require('./saveAssets');
|
const saveAssets = require('./saveAssets');
|
||||||
|
|
||||||
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
|
|
||||||
|
|
||||||
import type {RequestOptions, OutputOptions} from './types.flow';
|
import type {RequestOptions, OutputOptions} from './types.flow';
|
||||||
import type {ConfigT} from 'metro-config/src/configTypes.flow';
|
import type {ConfigT} from 'metro-config/src/configTypes.flow';
|
||||||
|
|
||||||
|
@ -48,6 +45,10 @@ async function buildBundle(
|
||||||
sourceMapUrl = path.basename(sourceMapUrl);
|
sourceMapUrl = path.basename(sourceMapUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.transformModulePath = args.transformer
|
||||||
|
? path.resolve(args.transformer)
|
||||||
|
: config.transformModulePath;
|
||||||
|
|
||||||
const requestOpts: RequestOptions = {
|
const requestOpts: RequestOptions = {
|
||||||
entryFile: args.entryFile,
|
entryFile: args.entryFile,
|
||||||
sourceMapUrl,
|
sourceMapUrl,
|
||||||
|
@ -56,13 +57,6 @@ async function buildBundle(
|
||||||
platform: args.platform,
|
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 {serverOptions} = convert.convertNewToOld(config);
|
||||||
|
|
||||||
const server = new Server(serverOptions);
|
const server = new Server(serverOptions);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('./core');
|
const {configPromise} = require('./core');
|
||||||
|
|
||||||
const assertRequiredOptions = require('./util/assertRequiredOptions');
|
const assertRequiredOptions = require('./util/assertRequiredOptions');
|
||||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
/* $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');
|
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)
|
const setupEnvScript = /^win/.test(process.platform)
|
||||||
? 'setup_env.bat'
|
? 'setup_env.bat'
|
||||||
: 'setup_env.sh';
|
: 'setup_env.sh';
|
||||||
|
|
|
@ -16,6 +16,7 @@ const findPlugins = require('./findPlugins');
|
||||||
const findAssets = require('./findAssets');
|
const findAssets = require('./findAssets');
|
||||||
const ios = require('./ios');
|
const ios = require('./ios');
|
||||||
const wrapCommands = require('./wrapCommands');
|
const wrapCommands = require('./wrapCommands');
|
||||||
|
const {ASSET_REGISTRY_PATH} = require('./Constants');
|
||||||
|
|
||||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
/* $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
|
* 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');
|
const path = require('path');
|
||||||
|
|
||||||
import type {CommandT} from '../commands';
|
import type {CommandT} from '../commands';
|
||||||
import type {ConfigT} from 'metro';
|
import type {ConfigT} from 'metro-config/src/configTypes.flow';
|
||||||
|
|
||||||
export type RNConfig = {
|
export type RNConfig = {
|
||||||
...ConfigT,
|
...ConfigT,
|
||||||
|
@ -36,10 +37,6 @@ export type RNConfig = {
|
||||||
* Returns an object with all platform configurations.
|
* Returns an object with all platform configurations.
|
||||||
*/
|
*/
|
||||||
getPlatformConfig(): Object,
|
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
|
* 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'),
|
hasteImplModulePath: require.resolve('../../jest/hasteImpl'),
|
||||||
|
|
||||||
getPlatforms(): Array<string> {
|
getPlatforms(): Array<string> {
|
||||||
|
@ -79,20 +76,9 @@ const defaultRNConfig = {
|
||||||
getProvidesModuleNodeModules(): Array<string> {
|
getProvidesModuleNodeModules(): Array<string> {
|
||||||
return ['react-native', 'react-native-windows'];
|
return ['react-native', 'react-native-windows'];
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
getProjectCommands(): Array<CommandT> {
|
const defaultRNConfig = {
|
||||||
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);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPlatformConfig(): Object {
|
getPlatformConfig(): Object {
|
||||||
return {
|
return {
|
||||||
ios,
|
ios,
|
||||||
|
@ -141,14 +127,33 @@ const defaultRNConfig = {
|
||||||
/**
|
/**
|
||||||
* Loads the CLI configuration
|
* Loads the CLI configuration
|
||||||
*/
|
*/
|
||||||
function getCliConfig(): RNConfig {
|
async function getCliConfig(): Promise<RNConfig> {
|
||||||
const cliArgs = minimist(process.argv.slice(2));
|
const cliArgs = minimist(process.argv.slice(2));
|
||||||
const config =
|
const config = await Config.load(path.resolve(__dirname, cliArgs.config));
|
||||||
cliArgs.config != null
|
|
||||||
? Config.load(path.resolve(__dirname, cliArgs.config))
|
config.transformer.assetRegistryPath = ASSET_REGISTRY_PATH;
|
||||||
: Config.findOptional(__dirname);
|
config.resolver.hasteImplModulePath = defaultConfig.hasteImplModulePath;
|
||||||
|
config.resolver.platforms = defaultConfig.getPlatforms();
|
||||||
|
config.resolver.providesModuleNodeModules = defaultConfig.getProvidesModuleNodeModules();
|
||||||
|
|
||||||
return {...defaultRNConfig, ...config};
|
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;
|
||||||
|
|
|
@ -16,8 +16,6 @@ const denodeify = require('denodeify');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
|
|
||||||
|
|
||||||
async function dependencies(argv, configPromise, args, packagerInstance) {
|
async function dependencies(argv, configPromise, args, packagerInstance) {
|
||||||
const rootModuleAbsolutePath = args.entryFile;
|
const rootModuleAbsolutePath = args.entryFile;
|
||||||
const config = await configPromise;
|
const config = await configPromise;
|
||||||
|
@ -31,12 +29,9 @@ async function dependencies(argv, configPromise, args, packagerInstance) {
|
||||||
config.transformModulePath = args.transformer
|
config.transformModulePath = args.transformer
|
||||||
? path.resolve(args.transformer)
|
? path.resolve(args.transformer)
|
||||||
: config.transformModulePath;
|
: config.transformModulePath;
|
||||||
config.transformer.transformModulePath = ASSET_REGISTRY_PATH;
|
|
||||||
|
|
||||||
const {serverOptions: packageOpts} = convert.convertNewToOld(config);
|
|
||||||
|
|
||||||
const relativePath = path.relative(
|
const relativePath = path.relative(
|
||||||
packageOpts.projectRoot,
|
config.projectRoot,
|
||||||
rootModuleAbsolutePath,
|
rootModuleAbsolutePath,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -53,10 +48,12 @@ async function dependencies(argv, configPromise, args, packagerInstance) {
|
||||||
? fs.createWriteStream(args.output)
|
? fs.createWriteStream(args.output)
|
||||||
: process.stdout;
|
: process.stdout;
|
||||||
|
|
||||||
|
const {serverOptions} = convert.convertNewToOld(config);
|
||||||
|
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
(packagerInstance
|
(packagerInstance
|
||||||
? packagerInstance.getOrderedDependencyPaths(options)
|
? packagerInstance.getOrderedDependencyPaths(options)
|
||||||
: Metro.getOrderedDependencyPaths(packageOpts, options)
|
: Metro.getOrderedDependencyPaths(serverOptions, options)
|
||||||
).then(deps => {
|
).then(deps => {
|
||||||
deps.forEach(modulePath => {
|
deps.forEach(modulePath => {
|
||||||
// Temporary hack to disable listing dependencies not under this directory.
|
// 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
|
// (a) JS code to not depend on anything outside this directory, or
|
||||||
// (b) Come up with a way to declare this dependency in Buck.
|
// (b) Come up with a way to declare this dependency in Buck.
|
||||||
const isInsideProjectRoots =
|
const isInsideProjectRoots =
|
||||||
packageOpts.watchFolders.filter(root => modulePath.startsWith(root))
|
config.watchFolders.filter(root => modulePath.startsWith(root))
|
||||||
.length > 0;
|
.length > 0;
|
||||||
|
|
||||||
if (isInsideProjectRoots) {
|
if (isInsideProjectRoots) {
|
||||||
|
|
|
@ -21,11 +21,8 @@ const morgan = require('morgan');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webSocketProxy = require('./util/webSocketProxy');
|
const webSocketProxy = require('./util/webSocketProxy');
|
||||||
const MiddlewareManager = require('./middleware/MiddlewareManager');
|
const MiddlewareManager = require('./middleware/MiddlewareManager');
|
||||||
const {convertOldToNew} = require('metro-config/src/convertConfig');
|
|
||||||
|
|
||||||
const {ASSET_REGISTRY_PATH} = require('../core/Constants');
|
import type {ConfigT} from 'metro-config/src/configTypes.flow';
|
||||||
|
|
||||||
import type {ConfigT} from 'metro';
|
|
||||||
|
|
||||||
export type Args = {|
|
export type Args = {|
|
||||||
+assetExts: $ReadOnlyArray<string>,
|
+assetExts: $ReadOnlyArray<string>,
|
||||||
|
@ -57,23 +54,14 @@ async function runServer(args: Args, config: ConfigT) {
|
||||||
|
|
||||||
args.watchFolders.forEach(middlewareManager.serveStatic);
|
args.watchFolders.forEach(middlewareManager.serveStatic);
|
||||||
|
|
||||||
const serverInstance = await Metro.runServer({
|
config.maxWorkers = args.maxWorkers;
|
||||||
config: convertOldToNew({
|
config.server.port = args.port;
|
||||||
config: {
|
config.reporter = reporter;
|
||||||
...config,
|
config.server.enhanceMiddleware = middleware =>
|
||||||
assetRegistryPath: ASSET_REGISTRY_PATH,
|
middlewareManager.getConnectInstance().use(middleware);
|
||||||
enhanceMiddleware: middleware =>
|
|
||||||
middlewareManager.getConnectInstance().use(middleware),
|
|
||||||
transformModulePath: args.transformer
|
|
||||||
? path.resolve(args.transformer)
|
|
||||||
: config.getTransformModulePath(),
|
|
||||||
},
|
|
||||||
maxWorkers: args.maxWorkers,
|
|
||||||
port: args.port,
|
|
||||||
reporter,
|
|
||||||
}),
|
|
||||||
|
|
||||||
hmrEnabled: true,
|
const serverInstance = await Metro.runServer({
|
||||||
|
config,
|
||||||
host: args.host,
|
host: args.host,
|
||||||
secure: args.https,
|
secure: args.https,
|
||||||
secureCert: args.cert,
|
secureCert: args.cert,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
const runServer = require('./runServer');
|
const runServer = require('./runServer');
|
||||||
|
|
||||||
import type {RNConfig} from '../core';
|
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';
|
import type {Args as RunServerArgs} from './runServer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +43,7 @@ module.exports = {
|
||||||
command: '--projectRoot [string]',
|
command: '--projectRoot [string]',
|
||||||
description: 'Specify the main project root',
|
description: 'Specify the main project root',
|
||||||
default: (config: ConfigT) => {
|
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',
|
'Specify any additional folders to be added to the watch list',
|
||||||
parse: (val: string) => val.split(','),
|
parse: (val: string) => val.split(','),
|
||||||
default: (config: ConfigT) => {
|
default: (config: ConfigT) => {
|
||||||
return config.getWatchFolders();
|
return config.watchFolders;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -60,21 +60,21 @@ module.exports = {
|
||||||
description:
|
description:
|
||||||
'Specify any additional asset extensions to be used by the packager',
|
'Specify any additional asset extensions to be used by the packager',
|
||||||
parse: (val: string) => val.split(','),
|
parse: (val: string) => val.split(','),
|
||||||
default: (config: ConfigT) => config.getAssetExts(),
|
default: (config: ConfigT) => config.resolver.assetExts,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: '--sourceExts [list]',
|
command: '--sourceExts [list]',
|
||||||
description:
|
description:
|
||||||
'Specify any additional source extensions to be used by the packager',
|
'Specify any additional source extensions to be used by the packager',
|
||||||
parse: (val: string) => val.split(','),
|
parse: (val: string) => val.split(','),
|
||||||
default: (config: ConfigT) => config.getSourceExts(),
|
default: (config: ConfigT) => config.resolver.sourceExts,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: '--platforms [list]',
|
command: '--platforms [list]',
|
||||||
description:
|
description:
|
||||||
'Specify any additional platforms to be used by the packager',
|
'Specify any additional platforms to be used by the packager',
|
||||||
parse: (val: string) => val.split(','),
|
parse: (val: string) => val.split(','),
|
||||||
default: (config: ConfigT) => config.getPlatforms(),
|
default: (config: ConfigT) => config.resolver.platforms,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: '--providesModuleNodeModules [list]',
|
command: '--providesModuleNodeModules [list]',
|
||||||
|
@ -82,10 +82,9 @@ module.exports = {
|
||||||
'Specify any npm packages that import dependencies with providesModule',
|
'Specify any npm packages that import dependencies with providesModule',
|
||||||
parse: (val: string) => val.split(','),
|
parse: (val: string) => val.split(','),
|
||||||
default: (config: RNConfig) => {
|
default: (config: RNConfig) => {
|
||||||
if (typeof config.getProvidesModuleNodeModules === 'function') {
|
return config.resolver
|
||||||
return config.getProvidesModuleNodeModules();
|
? config.resolver.providesModuleNodeModules
|
||||||
}
|
: undefined;
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,21 +10,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const findSymlinkedModules = require('./findSymlinkedModules');
|
const findSymlinkedModules = require('./findSymlinkedModules');
|
||||||
const fs = require('fs');
|
|
||||||
const getPolyfills = require('../../rn-get-polyfills');
|
const getPolyfills = require('../../rn-get-polyfills');
|
||||||
const invariant = require('fbjs/lib/invariant');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const {Config: MetroConfig, createBlacklist} = require('metro');
|
const {createBlacklist} = require('metro');
|
||||||
|
const {loadConfig, mergeConfig} = require('metro-config');
|
||||||
const RN_CLI_CONFIG = 'rn-cli.config.js';
|
|
||||||
|
|
||||||
import type {ConfigT as MetroConfigT} from 'metro';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration file of the CLI.
|
* Configuration file of the CLI.
|
||||||
*/
|
*/
|
||||||
export type ConfigT = MetroConfigT;
|
import type {ConfigT} from 'metro-config/src/configTypes.flow';
|
||||||
|
|
||||||
function getProjectPath() {
|
function getProjectPath() {
|
||||||
if (
|
if (
|
||||||
|
@ -70,68 +65,30 @@ const getBlacklistRE = () => {
|
||||||
* hierarchy, an error will be thrown.
|
* hierarchy, an error will be thrown.
|
||||||
*/
|
*/
|
||||||
const Config = {
|
const Config = {
|
||||||
DEFAULT: ({
|
DEFAULT: {
|
||||||
...MetroConfig.DEFAULT,
|
resolver: {
|
||||||
getBlacklistRE,
|
resolverMainFields: ['react-native', 'browser', 'main'],
|
||||||
getModulesRunBeforeMainModule: () => [
|
blacklistRE: getBlacklistRE(),
|
||||||
require.resolve('../../Libraries/Core/InitializeCore'),
|
},
|
||||||
],
|
serializer: {
|
||||||
getProjectRoots,
|
getModulesRunBeforeMainModule: () => [
|
||||||
getPolyfills,
|
require.resolve('../../Libraries/Core/InitializeCore'),
|
||||||
getWatchFolders: () => [getProjectPath()],
|
],
|
||||||
getResolverMainFields: () => ['react-native', 'browser', 'main'],
|
getPolyfills,
|
||||||
getTransformModulePath: () =>
|
},
|
||||||
require.resolve('metro/src/reactNativeTransformer'),
|
|
||||||
}: ConfigT),
|
|
||||||
|
|
||||||
find(startDir: string): ConfigT {
|
watchFolders: [getProjectPath(), ...getProjectRoots()],
|
||||||
return this.findWithPath(startDir).config;
|
transformModulePath: require.resolve('metro/src/reactNativeTransformer'),
|
||||||
},
|
|
||||||
|
|
||||||
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};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getProjectPath,
|
getProjectPath,
|
||||||
getProjectRoots,
|
getProjectRoots,
|
||||||
|
|
||||||
load(configFile: string): ConfigT {
|
async load(configFile: string): Promise<ConfigT> {
|
||||||
return MetroConfig.load(configFile, Config.DEFAULT);
|
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;
|
module.exports = Config;
|
||||||
|
|
Loading…
Reference in New Issue