diff --git a/packages/babel-cli/src/babel/index.js b/packages/babel-cli/src/babel/index.js index 48682c11fa..696c42fe18 100755 --- a/packages/babel-cli/src/babel/index.js +++ b/packages/babel-cli/src/babel/index.js @@ -238,10 +238,6 @@ if (errors.length) { // const opts = commander.opts(); -//the configFile CLI option maps to the extends option in the node API -if (opts.configFile) { - opts.extends = opts.configFile; -} // Delete options that are specific to @babel/cli and shouldn't be passed to @babel/core. delete opts.version; @@ -253,7 +249,6 @@ delete opts.outDir; delete opts.copyFiles; delete opts.includeDotfiles; delete opts.verbose; -delete opts.configFile; delete opts.deleteDirOnStart; delete opts.keepFileExtension; delete opts.relative; diff --git a/packages/babel-core/README.md b/packages/babel-core/README.md index 6499ad881c..aeb600c947 100644 --- a/packages/babel-core/README.md +++ b/packages/babel-core/README.md @@ -216,7 +216,10 @@ Following is a table of the options you can use: | `ast` | `false` | Include the AST in the returned object | | `auxiliaryCommentAfter` | `null` | Attach a comment after all non-user injected code | | `auxiliaryCommentBefore` | `null` | Attach a comment before all non-user injected code | +| `root` | `"."` | Specify the "root" folder that defines the location to search for "babel.config.js", and the default folder to allow `.babelrc` files inside of.| +| `configFile` | `undefined` | The config file to load Babel's config from. Defaults to searching for "babel.config.js" inside the "root" folder. `false` will disable searching for config files.| | `babelrc` | `true` | Specify whether or not to use .babelrc and .babelignore files. Not available when using the CLI, [use `--no-babelrc` instead](https://babeljs.io/docs/usage/cli/#babel-ignoring-babelrc) | +| `babelrcRoots` | `(root)` | Specify which packages should be search for .babelrc files when they are being compiled. `true` to _always_ search, or a path string or an array of paths to packages to search inside of. Defaults to only searching the "root" package. | | `envName` | env vars | Defaults to environment variable `BABEL_ENV` if set, or else `NODE_ENV` if set, or else it defaults to `"development"` | | `code` | `true` | Enable code generation | | `comments` | `true` | Output comments in generated output | diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js index cf0e3eaf01..1038652bc2 100644 --- a/packages/babel-core/src/config/config-chain.js +++ b/packages/babel-core/src/config/config-chain.js @@ -8,15 +8,19 @@ import { type ValidatedOptions, type IgnoreList, type ConfigApplicableTest, + type BabelrcSearch, } from "./validation/options"; const debug = buildDebug("babel:config:config-chain"); import { + findPackageData, findRelativeConfig, + findRootConfig, loadConfig, type ConfigFile, type IgnoreFile, + type FilePackageData, } from "./files"; import { makeWeakCache, makeStrongCache } from "./caching"; @@ -106,6 +110,7 @@ const loadPresetOverridesEnvDescriptors = makeWeakCache( export type RootConfigChain = ConfigChain & { babelrc: ConfigFile | void, + config: ConfigFile | void, ignore: IgnoreFile | void, }; @@ -125,24 +130,57 @@ export function buildRootChain( ); if (!programmaticChain) return null; - let ignore, babelrc; + const { + root: rootDir = ".", + babelrc = true, + babelrcRoots, + configFile: configFileName = true, + } = opts; + const absoluteRoot = path.resolve(context.cwd, rootDir); + + let configFile; + if (typeof configFileName === "string") { + configFile = loadConfig(configFileName, context.cwd, context.envName); + } else if (configFileName === true) { + configFile = findRootConfig(absoluteRoot, context.envName); + } + + const configFileChain = emptyChain(); + if (configFile) { + const result = loadFileChain(configFile, context); + if (!result) return null; + + mergeChain(configFileChain, result); + } + + const pkgData = + typeof context.filename === "string" + ? findPackageData(context.filename) + : null; + + let ignoreFile, babelrcFile; const fileChain = emptyChain(); // resolve all .babelrc files - if (opts.babelrc !== false && context.filename !== null) { - const filename = context.filename; - - ({ ignore, config: babelrc } = findRelativeConfig( - filename, + if ( + babelrc && + pkgData && + babelrcLoadEnabled(context, pkgData, babelrcRoots, absoluteRoot) + ) { + ({ ignore: ignoreFile, config: babelrcFile } = findRelativeConfig( + pkgData, context.envName, )); - if (ignore && shouldIgnore(context, ignore.ignore, null, ignore.dirname)) { + if ( + ignoreFile && + shouldIgnore(context, ignoreFile.ignore, null, ignoreFile.dirname) + ) { return null; } - if (babelrc) { - const result = loadFileChain(babelrc, context); + if (babelrcFile) { + const result = loadFileChain(babelrcFile, context); if (!result) return null; mergeChain(fileChain, result); @@ -152,7 +190,7 @@ export function buildRootChain( // Insert file chain in front so programmatic options have priority // over configuration file chain items. const chain = mergeChain( - mergeChain(emptyChain(), fileChain), + mergeChain(mergeChain(emptyChain(), configFileChain), fileChain), programmaticChain, ); @@ -160,11 +198,39 @@ export function buildRootChain( plugins: dedupDescriptors(chain.plugins), presets: dedupDescriptors(chain.presets), options: chain.options.map(o => normalizeOptions(o)), - ignore: ignore || undefined, - babelrc: babelrc || undefined, + ignore: ignoreFile || undefined, + babelrc: babelrcFile || undefined, + config: configFile || undefined, }; } +function babelrcLoadEnabled( + context: ConfigContext, + pkgData: FilePackageData, + babelrcRoots: BabelrcSearch | void, + absoluteRoot: string, +): boolean { + if (typeof babelrcRoots === "boolean") return babelrcRoots; + + // Fast path to avoid having to load micromatch if the babelrc is just + // loading in the standard root directory. + if (babelrcRoots === undefined) { + return pkgData.directories.indexOf(absoluteRoot) !== -1; + } + + let babelrcPatterns = babelrcRoots; + if (!Array.isArray(babelrcPatterns)) babelrcPatterns = [babelrcPatterns]; + babelrcPatterns = babelrcPatterns.map(pat => path.resolve(context.cwd, pat)); + + // Fast path to avoid having to load micromatch if the babelrc is just + // loading in the standard root directory. + if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) { + return pkgData.directories.indexOf(absoluteRoot) !== -1; + } + + return micromatch(pkgData.directories, babelrcPatterns).length > 0; +} + /** * Build a config chain for just the programmatic options passed into Babel. */ diff --git a/packages/babel-core/src/config/files/configuration.js b/packages/babel-core/src/config/files/configuration.js index c1a55cc598..e7022ce2bc 100644 --- a/packages/babel-core/src/config/files/configuration.js +++ b/packages/babel-core/src/config/files/configuration.js @@ -5,45 +5,35 @@ import path from "path"; import fs from "fs"; import json5 from "json5"; import resolve from "resolve"; -import { makeStrongCache, type CacheConfigurator } from "../caching"; +import { + makeStrongCache, + makeWeakCache, + type CacheConfigurator, +} from "../caching"; import makeAPI from "../helpers/config-api"; +import { makeStaticFileCache } from "./utils"; +import type { FilePackageData, RelativeConfig, ConfigFile } from "./types"; const debug = buildDebug("babel:config:loading:files:configuration"); -export type ConfigFile = { - filepath: string, - dirname: string, - options: {}, -}; - -export type IgnoreFile = { - filepath: string, - dirname: string, - ignore: Array, -}; - -export type RelativeConfig = { - config: ConfigFile | null, - ignore: IgnoreFile | null, -}; +const BABEL_CONFIG_JS_FILENAME = "babel.config.js"; const BABELRC_FILENAME = ".babelrc"; const BABELRC_JS_FILENAME = ".babelrc.js"; -const PACKAGE_FILENAME = "package.json"; const BABELIGNORE_FILENAME = ".babelignore"; export function findRelativeConfig( - filepath: string, + packageData: FilePackageData, envName: string, ): RelativeConfig { let config = null; let ignore = null; - const dirname = path.dirname(filepath); - let loc = dirname; - while (true) { + const dirname = path.dirname(packageData.filepath); + + for (const loc of packageData.directories) { if (!config) { - config = [BABELRC_FILENAME, BABELRC_JS_FILENAME, PACKAGE_FILENAME].reduce( + config = [BABELRC_FILENAME, BABELRC_JS_FILENAME].reduce( (previousConfig: ConfigFile | null, name) => { const filepath = path.join(loc, name); const config = readConfig(filepath, envName); @@ -62,6 +52,23 @@ export function findRelativeConfig( null, ); + const pkgConfig = + packageData.pkg && packageData.pkg.dirname === loc + ? packageToBabelConfig(packageData.pkg) + : null; + + if (pkgConfig) { + if (config) { + throw new Error( + `Multiple configuration files found. Please remove one:\n` + + ` - ${path.basename(pkgConfig.filepath)}#babel\n` + + ` - ${path.basename(config.filepath)}\n` + + `from ${loc}`, + ); + } + config = pkgConfig; + } + if (config) { debug("Found configuration %o from %o.", config.filepath, dirname); } @@ -75,15 +82,24 @@ export function findRelativeConfig( debug("Found ignore %o from %o.", ignore.filepath, dirname); } } - - const nextLoc = path.dirname(loc); - if (loc === nextLoc) break; - loc = nextLoc; } return { config, ignore }; } +export function findRootConfig( + dirname: string, + envName: string, +): ConfigFile | null { + const filepath = path.resolve(dirname, BABEL_CONFIG_JS_FILENAME); + + const conf = readConfig(filepath, envName); + if (conf) { + debug("Found root config %o in $o.", BABEL_CONFIG_JS_FILENAME, dirname); + } + return conf; +} + export function loadConfig( name: string, dirname: string, @@ -107,7 +123,7 @@ export function loadConfig( function readConfig(filepath, envName): ConfigFile | null { return path.extname(filepath) === ".js" ? readConfigJS(filepath, { envName }) - : readConfigFile(filepath); + : readConfigJSON5(filepath); } const LOADING_CONFIGS = new Set(); @@ -180,27 +196,34 @@ const readConfigJS = makeStrongCache( }, ); -const readConfigFile = makeStaticFileCache((filepath, content) => { - let options; - if (path.basename(filepath) === PACKAGE_FILENAME) { - try { - options = JSON.parse(content).babel; - } catch (err) { - err.message = `${filepath}: Error while parsing JSON - ${err.message}`; - throw err; - } - if (!options) return null; - } else { - try { - options = json5.parse(content); - } catch (err) { - err.message = `${filepath}: Error while parsing config - ${err.message}`; - throw err; +const packageToBabelConfig = makeWeakCache( + (file: ConfigFile): ConfigFile | null => { + if (typeof file.options.babel === "undefined") return null; + const babel = file.options.babel; + + if (typeof babel !== "object" || Array.isArray(babel) || babel === null) { + throw new Error(`${file.filepath}: .babel property must be an object`); } - if (!options) throw new Error(`${filepath}: No config detected`); + return { + filepath: file.filepath, + dirname: file.dirname, + options: babel, + }; + }, +); + +const readConfigJSON5 = makeStaticFileCache((filepath, content) => { + let options; + try { + options = json5.parse(content); + } catch (err) { + err.message = `${filepath}: Error while parsing config - ${err.message}`; + throw err; } + if (!options) throw new Error(`${filepath}: No config detected`); + if (typeof options !== "object") { throw new Error(`${filepath}: Config returned typeof ${typeof options}`); } @@ -228,27 +251,6 @@ const readIgnoreConfig = makeStaticFileCache((filepath, content) => { }; }); -function makeStaticFileCache(fn: (string, string) => T): string => T | null { - return makeStrongCache((filepath, cache) => { - if (cache.invalidate(() => fileMtime(filepath)) === null) { - cache.forever(); - return null; - } - - return fn(filepath, fs.readFileSync(filepath, "utf8")); - }); -} - -function fileMtime(filepath: string): number | null { - try { - return +fs.statSync(filepath).mtime; - } catch (e) { - if (e.code !== "ENOENT") throw e; - } - - return null; -} - function throwConfigError() { throw new Error(`\ Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured diff --git a/packages/babel-core/src/config/files/index-browser.js b/packages/babel-core/src/config/files/index-browser.js index 4e5cb5fe62..baa0fd43d8 100644 --- a/packages/babel-core/src/config/files/index-browser.js +++ b/packages/babel-core/src/config/files/index-browser.js @@ -1,17 +1,42 @@ // @flow -import type { ConfigFile, IgnoreFile, RelativeConfig } from "./configuration"; +import type { + ConfigFile, + IgnoreFile, + RelativeConfig, + FilePackageData, +} from "./types"; -export type { ConfigFile, IgnoreFile, RelativeConfig }; +export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData }; -export function findRelativeConfig( - filepath: string, - envName: string, // eslint-disable-line no-unused-vars -): RelativeConfig { - return { config: null, ignore: null }; +export function findPackageData(filepath: string): FilePackageData { + return { + filepath, + directories: [], + pkg: null, + isPackage: false, + }; } -export function loadConfig(name: string, dirname: string): ConfigFile { +export function findRelativeConfig( + pkgData: FilePackageData, + envName: string, // eslint-disable-line no-unused-vars +): RelativeConfig { + return { pkg: null, config: null, ignore: null }; +} + +export function findRootConfig( + dirname: string, + envName: string, // eslint-disable-line no-unused-vars +): ConfigFile | null { + return null; +} + +export function loadConfig( + name: string, + dirname: string, + envName: string, // eslint-disable-line no-unused-vars +): ConfigFile { throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`); } diff --git a/packages/babel-core/src/config/files/index.js b/packages/babel-core/src/config/files/index.js index c7e61ad469..9f8d9797bb 100644 --- a/packages/babel-core/src/config/files/index.js +++ b/packages/babel-core/src/config/files/index.js @@ -7,5 +7,22 @@ import typeof * as indexType from "./index"; // exports of index-browser, since this file may be replaced at bundle time with index-browser. ((({}: any): $Exact): $Exact); -export * from "./configuration"; -export * from "./plugins"; +export { findPackageData } from "./package"; + +export { + findRelativeConfig, + findRootConfig, + loadConfig, +} from "./configuration"; +export type { + ConfigFile, + IgnoreFile, + RelativeConfig, + FilePackageData, +} from "./types"; +export { + resolvePlugin, + resolvePreset, + loadPlugin, + loadPreset, +} from "./plugins"; diff --git a/packages/babel-core/src/config/files/package.js b/packages/babel-core/src/config/files/package.js new file mode 100644 index 0000000000..8e370a472d --- /dev/null +++ b/packages/babel-core/src/config/files/package.js @@ -0,0 +1,60 @@ +// @flow + +import path from "path"; +import { makeStaticFileCache } from "./utils"; + +import type { ConfigFile, FilePackageData } from "./types"; + +const PACKAGE_FILENAME = "package.json"; + +/** + * Find metadata about the package that this file is inside of. Resolution + * of Babel's config requires general package information to decide when to + * search for .babelrc files + */ +export function findPackageData(filepath: string): FilePackageData { + let pkg = null; + const directories = []; + let isPackage = true; + + let dirname = path.dirname(filepath); + while (!pkg && path.basename(dirname) !== "node_modules") { + directories.push(dirname); + + pkg = readConfigPackage(path.join(dirname, PACKAGE_FILENAME)); + + const nextLoc = path.dirname(dirname); + if (dirname === nextLoc) { + isPackage = false; + break; + } + dirname = nextLoc; + } + + return { filepath, directories, pkg, isPackage }; +} + +const readConfigPackage = makeStaticFileCache( + (filepath, content): ConfigFile => { + let options; + try { + options = JSON.parse(content); + } catch (err) { + err.message = `${filepath}: Error while parsing JSON - ${err.message}`; + throw err; + } + + if (typeof options !== "object") { + throw new Error(`${filepath}: Config returned typeof ${typeof options}`); + } + if (Array.isArray(options)) { + throw new Error(`${filepath}: Expected config object but found array`); + } + + return { + filepath, + dirname: path.dirname(filepath), + options, + }; + }, +); diff --git a/packages/babel-core/src/config/files/types.js b/packages/babel-core/src/config/files/types.js new file mode 100644 index 0000000000..fa9d96fe5d --- /dev/null +++ b/packages/babel-core/src/config/files/types.js @@ -0,0 +1,38 @@ +// @flow + +export type ConfigFile = { + filepath: string, + dirname: string, + options: {}, +}; + +export type IgnoreFile = { + filepath: string, + dirname: string, + ignore: Array, +}; + +export type RelativeConfig = { + // The actual config, either from package.json#babel, .babelrc, or + // .babelrc.js, if there was one. + config: ConfigFile | null, + + // The .babelignore, if there was one. + ignore: IgnoreFile | null, +}; + +export type FilePackageData = { + // The file in the package. + filepath: string, + + // Any ancestor directories of the file that are within the package. + directories: Array, + + // The contents of the package.json. May not be found if the package just + // terminated at a node_modules folder without finding one. + pkg: ConfigFile | null, + + // True if a package.json or node_modules folder was found while traversing + // the directory structure. + isPackage: boolean, +}; diff --git a/packages/babel-core/src/config/files/utils.js b/packages/babel-core/src/config/files/utils.js new file mode 100644 index 0000000000..db202259d5 --- /dev/null +++ b/packages/babel-core/src/config/files/utils.js @@ -0,0 +1,27 @@ +// @flow + +import fs from "fs"; +import { makeStrongCache } from "../caching"; + +export function makeStaticFileCache( + fn: (string, string) => T, +): string => T | null { + return makeStrongCache((filepath, cache) => { + if (cache.invalidate(() => fileMtime(filepath)) === null) { + cache.forever(); + return null; + } + + return fn(filepath, fs.readFileSync(filepath, "utf8")); + }); +} + +function fileMtime(filepath: string): number | null { + try { + return +fs.statSync(filepath).mtime; + } catch (e) { + if (e.code !== "ENOENT" && e.code !== "ENOTDIR") throw e; + } + + return null; +} diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index 83d0d08086..2a491f5e69 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -17,6 +17,7 @@ export default function loadPrivatePartialConfig( context: ConfigContext, ignore: IgnoreFile | void, babelrc: ConfigFile | void, + config: ConfigFile | void, } | null { if ( inputOpts != null && @@ -64,6 +65,7 @@ export default function loadPrivatePartialConfig( context, ignore: configChain.ignore, babelrc: configChain.babelrc, + config: configChain.config, }; } @@ -71,7 +73,7 @@ export function loadPartialConfig(inputOpts: mixed): PartialConfig | null { const result = loadPrivatePartialConfig(inputOpts); if (!result) return null; - const { options, babelrc, ignore } = result; + const { options, babelrc, ignore, config } = result; (options.plugins || []).forEach(item => { if (item.value instanceof Plugin) { @@ -86,6 +88,7 @@ export function loadPartialConfig(inputOpts: mixed): PartialConfig | null { options, babelrc ? babelrc.filepath : undefined, ignore ? ignore.filepath : undefined, + config ? config.filepath : undefined, ); } @@ -99,15 +102,18 @@ class PartialConfig { options: ValidatedOptions; babelrc: string | void; babelignore: string | void; + config: string | void; constructor( options: ValidatedOptions, babelrc: string | void, ignore: string | void, + config: string | void, ) { this.options = options; this.babelignore = ignore; this.babelrc = babelrc; + this.config = config; // Freeze since this is a public API and it should be extremely obvious that // reassigning properties on here does nothing. @@ -122,7 +128,7 @@ class PartialConfig { * this.babelrc directly. */ hasFilesystemConfig(): boolean { - return this.babelrc !== undefined; + return this.babelrc !== undefined || this.config !== undefined; } } Object.freeze(PartialConfig.prototype); diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js index 97b860f016..a637651700 100644 --- a/packages/babel-core/src/config/validation/option-assertions.js +++ b/packages/babel-core/src/config/validation/option-assertions.js @@ -1,6 +1,8 @@ // @flow import type { + ConfigFileSearch, + BabelrcSearch, IgnoreList, IgnoreItem, PluginList, @@ -164,6 +166,45 @@ function checkValidTest(value: mixed): boolean { ); } +export function assertConfigFileSearch( + key: string, + value: mixed, +): ConfigFileSearch | void { + if ( + value !== undefined && + typeof value !== "boolean" && + typeof value !== "string" + ) { + throw new Error( + `.${key} must be a undefined, a boolean, a string, ` + + `got ${JSON.stringify(value)}`, + ); + } + + return value; +} + +export function assertBabelrcSearch( + key: string, + value: mixed, +): BabelrcSearch | void { + if (value === undefined || typeof value === "boolean") return value; + + if (Array.isArray(value)) { + value.forEach((item, i) => { + if (typeof item !== "string") { + throw new Error(`.${key}[${i}] must be a string.`); + } + }); + } else if (typeof value !== "string") { + throw new Error( + `.${key} must be a undefined, a boolean, a string, ` + + `or an array of strings, got ${JSON.stringify(value)}`, + ); + } + return (value: any); +} + export function assertPluginList(key: string, value: mixed): PluginList | void { const arr = assertArray(key, value); if (arr) { diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 290d326d5b..4b6024cf03 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -13,6 +13,8 @@ import { assertIgnoreList, assertPluginList, assertConfigApplicableTest, + assertConfigFileSearch, + assertBabelrcSearch, assertFunction, assertSourceMaps, assertCompact, @@ -23,6 +25,11 @@ import { const ROOT_VALIDATORS: ValidatorSet = { cwd: (assertString: Validator<$PropertyType>), + root: (assertString: Validator<$PropertyType>), + configFile: (assertConfigFileSearch: Validator< + $PropertyType, + >), + filename: (assertString: Validator< $PropertyType, >), @@ -32,6 +39,9 @@ const ROOT_VALIDATORS: ValidatorSet = { babelrc: (assertBoolean: Validator< $PropertyType, >), + babelrcRoots: (assertBabelrcSearch: Validator< + $PropertyType, + >), code: (assertBoolean: Validator<$PropertyType>), ast: (assertBoolean: Validator<$PropertyType>), @@ -151,6 +161,9 @@ export type ValidatedOptions = { filename?: string, filenameRelative?: string, babelrc?: boolean, + babelrcRoots?: BabelrcSearch, + configFile?: ConfigFileSearch, + root?: string, code?: boolean, ast?: boolean, inputSourceMap?: RootInputSourceMapOption, @@ -223,6 +236,8 @@ export type PluginList = $ReadOnlyArray; export type OverridesList = Array; export type ConfigApplicableTest = IgnoreItem | Array; +export type ConfigFileSearch = string | boolean; +export type BabelrcSearch = boolean | string | Array; export type SourceMapsOption = boolean | "inline" | "both"; export type SourceTypeOption = "module" | "script" | "unambiguous"; export type CompactOption = boolean | "auto"; diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index 101a184089..0977bad100 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -294,6 +294,7 @@ describe("api", function() { process.env.BABEL_ENV = "development"; const result = babel.transform("", { + cwd: path.join(__dirname, "fixtures", "config", "complex-plugin-config"), filename: path.join( __dirname, "fixtures", diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index fc3b344968..8aa83a7481 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -11,6 +11,7 @@ describe("buildConfigChain", function() { describe("single", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: fixture("nonexistant-fake"), @@ -22,6 +23,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: new RegExp(fixture("nonexistant-fake")), @@ -33,6 +35,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: p => p.indexOf(fixture("nonexistant-fake")) === 0, @@ -44,6 +47,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: fixture("nonexistant-fake-unknown"), @@ -55,6 +59,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: new RegExp(fixture("nonexistant-unknown")), @@ -66,6 +71,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: p => p.indexOf(fixture("nonexistant-unknown")) === 0, @@ -79,6 +85,7 @@ describe("buildConfigChain", function() { describe("array", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [fixture("nonexistant-fake")], @@ -90,6 +97,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [new RegExp(fixture("nonexistant-fake"))], @@ -101,6 +109,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [p => p.indexOf(fixture("nonexistant-fake")) === 0], @@ -112,6 +121,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [fixture("nonexistant-fake-unknown")], @@ -123,6 +133,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [new RegExp(fixture("nonexistant-unknown"))], @@ -134,6 +145,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, test: [p => p.indexOf(fixture("nonexistant-unknown")) === 0], @@ -149,6 +161,7 @@ describe("buildConfigChain", function() { describe("single", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: fixture("nonexistant-fake"), @@ -160,6 +173,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: new RegExp(fixture("nonexistant-fake")), @@ -171,6 +185,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: p => p.indexOf(fixture("nonexistant-fake")) === 0, @@ -182,6 +197,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: fixture("nonexistant-fake-unknown"), @@ -193,6 +209,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: new RegExp(fixture("nonexistant-unknown")), @@ -204,6 +221,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: p => p.indexOf(fixture("nonexistant-unknown")) === 0, @@ -217,6 +235,7 @@ describe("buildConfigChain", function() { describe("array", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [fixture("nonexistant-fake")], @@ -228,6 +247,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [new RegExp(fixture("nonexistant-fake"))], @@ -239,6 +259,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [p => p.indexOf(fixture("nonexistant-fake")) === 0], @@ -250,6 +271,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [fixture("nonexistant-fake-unknown")], @@ -261,6 +283,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [new RegExp(fixture("nonexistant-unknown"))], @@ -272,6 +295,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, include: [p => p.indexOf(fixture("nonexistant-unknown")) === 0], @@ -287,6 +311,7 @@ describe("buildConfigChain", function() { describe("single", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: fixture("nonexistant-fake"), @@ -298,6 +323,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: new RegExp(fixture("nonexistant-fake")), @@ -309,6 +335,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: p => p.indexOf(fixture("nonexistant-fake")) === 0, @@ -320,6 +347,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: fixture("nonexistant-fake-unknown"), @@ -331,6 +359,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: new RegExp(fixture("nonexistant-unknown")), @@ -342,6 +371,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: p => p.indexOf(fixture("nonexistant-unknown")) === 0, @@ -355,6 +385,7 @@ describe("buildConfigChain", function() { describe("array", () => { it("should process matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [fixture("nonexistant-fake")], @@ -366,6 +397,7 @@ describe("buildConfigChain", function() { it("should process matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [new RegExp(fixture("nonexistant-fake"))], @@ -377,6 +409,7 @@ describe("buildConfigChain", function() { it("should process matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [p => p.indexOf(fixture("nonexistant-fake")) === 0], @@ -388,6 +421,7 @@ describe("buildConfigChain", function() { it("should process non-matching string values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [fixture("nonexistant-fake-unknown")], @@ -399,6 +433,7 @@ describe("buildConfigChain", function() { it("should process non-matching RegExp values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [new RegExp(fixture("nonexistant-unknown"))], @@ -410,6 +445,7 @@ describe("buildConfigChain", function() { it("should process non-matching function values", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, exclude: [p => p.indexOf(fixture("nonexistant-unknown")) === 0], @@ -424,6 +460,7 @@ describe("buildConfigChain", function() { describe("ignore", () => { it("should ignore files that match", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, ignore: [ @@ -441,6 +478,7 @@ describe("buildConfigChain", function() { it("should not ignore files that don't match", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, ignore: [ @@ -456,6 +494,7 @@ describe("buildConfigChain", function() { describe("only", () => { it("should ignore files that don't match", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, only: [ @@ -469,6 +508,7 @@ describe("buildConfigChain", function() { it("should not ignore files that match", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, only: [ @@ -484,6 +524,7 @@ describe("buildConfigChain", function() { describe("ignore/only", () => { it("should ignore files that match ignore and don't match only", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, ignore: [fixture("nonexistant-fake", "src.js")], @@ -495,6 +536,7 @@ describe("buildConfigChain", function() { it("should ignore files that match ignore and also only", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, ignore: [fixture("nonexistant-fake", "src.js")], @@ -506,6 +548,7 @@ describe("buildConfigChain", function() { it("should not ignore files that match only and not ignore", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, only: [fixture("nonexistant-fake", "src.js")], @@ -516,6 +559,7 @@ describe("buildConfigChain", function() { it("should not ignore files when no ignore/only are specified", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, }); @@ -525,6 +569,7 @@ describe("buildConfigChain", function() { it("should allow negation of only", () => { const opts1 = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, only: [ @@ -535,6 +580,7 @@ describe("buildConfigChain", function() { expect(opts1).toBeNull(); const opts2 = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, only: [ @@ -545,6 +591,7 @@ describe("buildConfigChain", function() { expect(opts2).not.toBeNull(); const opts3 = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "folder", "src.js"), babelrc: false, only: [ @@ -557,6 +604,7 @@ describe("buildConfigChain", function() { it("should allow negation of ignore", () => { const opts1 = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, ignore: [ @@ -568,6 +616,7 @@ describe("buildConfigChain", function() { // Tests disabled pending https://github.com/babel/babel/issues/6907 // const opts2 = loadOptions({ + // cwd: fixture("nonexistant-fake"), // filename: fixture("nonexistant-fake", "src.js"), // babelrc: false, // ignore: [ @@ -578,6 +627,7 @@ describe("buildConfigChain", function() { // expect(opts2).not.toBeNull(); // // const opts3 = loadOptions({ + // cwd: fixture("nonexistant-fake"), // filename: fixture("nonexistant-fake", "folder", "src.js"), // babelrc: false, // ignore: [ @@ -718,13 +768,13 @@ describe("buildConfigChain", function() { "package.json", ); - const opts1 = loadOptions({ filename }); - const opts2 = loadOptions({ filename }); + const opts1 = loadOptions({ filename, cwd: path.dirname(filename) }); + const opts2 = loadOptions({ filename, cwd: path.dirname(filename) }); touch(pkgJSON); - const opts3 = loadOptions({ filename }); - const opts4 = loadOptions({ filename }); + const opts3 = loadOptions({ filename, cwd: path.dirname(filename) }); + const opts4 = loadOptions({ filename, cwd: path.dirname(filename) }); expect(opts1.plugins).toHaveLength(1); expect(opts2.plugins).toHaveLength(1); @@ -752,13 +802,13 @@ describe("buildConfigChain", function() { ".babelrc", ); - const opts1 = loadOptions({ filename }); - const opts2 = loadOptions({ filename }); + const opts1 = loadOptions({ filename, cwd: path.dirname(filename) }); + const opts2 = loadOptions({ filename, cwd: path.dirname(filename) }); touch(babelrcFile); - const opts3 = loadOptions({ filename }); - const opts4 = loadOptions({ filename }); + const opts3 = loadOptions({ filename, cwd: path.dirname(filename) }); + const opts4 = loadOptions({ filename, cwd: path.dirname(filename) }); expect(opts1.plugins).toHaveLength(1); expect(opts2.plugins).toHaveLength(1); @@ -780,11 +830,19 @@ describe("buildConfigChain", function() { "src.js", ); - const opts1 = loadOptions({ filename }); - const opts2 = loadOptions({ filename }); + const opts1 = loadOptions({ filename, cwd: path.dirname(filename) }); + const opts2 = loadOptions({ filename, cwd: path.dirname(filename) }); - const opts3 = loadOptions({ filename, envName: "new-env" }); - const opts4 = loadOptions({ filename, envName: "new-env" }); + const opts3 = loadOptions({ + filename, + envName: "new-env", + cwd: path.dirname(filename), + }); + const opts4 = loadOptions({ + filename, + envName: "new-env", + cwd: path.dirname(filename), + }); expect(opts1.plugins).toHaveLength(1); expect(opts2.plugins).toHaveLength(1); @@ -803,6 +861,7 @@ describe("buildConfigChain", function() { describe("overrides merging", () => { it("should apply matching overrides over base configs", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, comments: true, @@ -819,6 +878,7 @@ describe("buildConfigChain", function() { it("should not apply non-matching overrides over base configs", () => { const opts = loadOptions({ + cwd: fixture("nonexistant-fake"), filename: fixture("nonexistant-fake", "src.js"), babelrc: false, comments: true, @@ -860,9 +920,15 @@ describe("buildConfigChain", function() { it("should load .babelrc", () => { const filename = fixture("config-files", "babelrc", "src.js"); - expect(loadOptions({ filename })).toEqual({ + expect( + loadOptions({ + filename, + cwd: path.dirname(filename), + }), + ).toEqual({ ...getDefaults(), filename, + cwd: path.dirname(filename), comments: true, }); }); @@ -870,9 +936,10 @@ describe("buildConfigChain", function() { it("should load .babelrc.js", () => { const filename = fixture("config-files", "babelrc-js", "src.js"); - expect(loadOptions({ filename })).toEqual({ + expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({ ...getDefaults(), filename, + cwd: path.dirname(filename), comments: true, }); }); @@ -880,9 +947,10 @@ describe("buildConfigChain", function() { it("should load package.json#babel", () => { const filename = fixture("config-files", "pkg", "src.js"); - expect(loadOptions({ filename })).toEqual({ + expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({ ...getDefaults(), filename, + cwd: path.dirname(filename), comments: true, }); }); @@ -890,39 +958,40 @@ describe("buildConfigChain", function() { it("should load .babelignore", () => { const filename = fixture("config-files", "babelignore", "src.js"); - expect(loadOptions({ filename })).toBeNull(); + expect(loadOptions({ filename, cwd: path.dirname(filename) })).toBeNull(); }); it("should throw if there are both .babelrc and .babelrc.js", () => { const filename = fixture("config-files", "both-babelrc", "src.js"); - expect(() => loadOptions({ filename })).toThrow( - /Multiple configuration files found/, - ); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Multiple configuration files found/); }); it("should throw if there are both .babelrc and package.json", () => { const filename = fixture("config-files", "pkg-babelrc", "src.js"); - expect(() => loadOptions({ filename })).toThrow( - /Multiple configuration files found/, - ); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Multiple configuration files found/); }); it("should throw if there are both .babelrc.js and package.json", () => { const filename = fixture("config-files", "pkg-babelrc-js", "src.js"); - expect(() => loadOptions({ filename })).toThrow( - /Multiple configuration files found/, - ); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Multiple configuration files found/); }); it("should ignore package.json without a 'babel' property", () => { const filename = fixture("config-files", "pkg-ignored", "src.js"); - expect(loadOptions({ filename })).toEqual({ + expect(loadOptions({ filename, cwd: path.dirname(filename) })).toEqual({ ...getDefaults(), filename, + cwd: path.dirname(filename), comments: true, }); }); @@ -930,23 +999,25 @@ describe("buildConfigChain", function() { it("should show helpful errors for .babelrc", () => { const filename = fixture("config-files", "babelrc-error", "src.js"); - expect(() => loadOptions({ filename })).toThrow( - /Error while parsing config - /, - ); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Error while parsing config - /); }); it("should show helpful errors for .babelrc.js", () => { const filename = fixture("config-files", "babelrc-js-error", "src.js"); - expect(() => loadOptions({ filename })).toThrow(/Babelrc threw an error/); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Babelrc threw an error/); }); it("should show helpful errors for package.json", () => { const filename = fixture("config-files", "pkg-error", "src.js"); - expect(() => loadOptions({ filename })).toThrow( - /Error while parsing JSON - /, - ); + expect(() => + loadOptions({ filename, cwd: path.dirname(filename) }), + ).toThrow(/Error while parsing JSON - /); }); }); }); diff --git a/packages/babel-core/test/config-loading.js b/packages/babel-core/test/config-loading.js index c2787f39c2..65eb0c6034 100644 --- a/packages/babel-core/test/config-loading.js +++ b/packages/babel-core/test/config-loading.js @@ -25,6 +25,7 @@ describe("@babel/core config loading", () => { function makeOpts(skipProgrammatic = false) { return { + cwd: path.dirname(FILEPATH), filename: FILEPATH, presets: skipProgrammatic ? null diff --git a/packages/babel-preset-env/test/debug-fixtures.js b/packages/babel-preset-env/test/debug-fixtures.js index d716ae55ba..6c746ed306 100644 --- a/packages/babel-preset-env/test/debug-fixtures.js +++ b/packages/babel-preset-env/test/debug-fixtures.js @@ -52,7 +52,9 @@ const buildTest = opts => { let args = [binLoc]; args = args.concat(opts.args); - const spawn = child.spawn(process.execPath, args); + const spawn = child.spawn(process.execPath, args, { + cwd: tmpLoc, + }); let stdout = ""; let stderr = ""; diff --git a/packages/babel-register/test/index.js b/packages/babel-register/test/index.js index 67eef99204..5643bdcf6f 100644 --- a/packages/babel-register/test/index.js +++ b/packages/babel-register/test/index.js @@ -1,4 +1,5 @@ import fs from "fs"; +import path from "path"; let currentHook; let currentOptions; @@ -39,6 +40,11 @@ describe("@babel/register", function() { let babelRegister; function setupRegister(config = { babelrc: false }) { + config = { + cwd: path.dirname(testFile), + ...config, + }; + babelRegister = require(registerFile); babelRegister.default(config); }