Files
babel/packages/babel-core/src/config/partial.js
Logan Smyth 69cca412eb Make the filename option, as exposed to the plugins, consistently relative to the working directory (#7956)
| Q                        | A <!--(Can use an emoji 👍) -->
| ------------------------ | ---
| Fixed Issues?            | 
| Patch: Bug Fix?          | Y
| Major: Breaking Change?  | N
| Minor: New Feature?      |
| Tests Added + Pass?      | Yes
| Documentation PR         | <!-- If so, add `[skip ci]` to your commit message to skip CI -->
| Any Dependency Changes?  |
| License                  | MIT

Currently the `opts.filename` value exposed to plugins is just whatever the user passed. While it _could_ be relative to the working directory, if Babel was passed an absolute URL, it'll be absolute.

This PR explicitly ensures the filename is a relative path based on the working directory. This also exposes an officially endorsed API for reading the working directory path.
2018-05-21 17:10:57 -07:00

146 lines
4.0 KiB
JavaScript

// @flow
import path from "path";
import Plugin from "./plugin";
import { mergeOptions } from "./util";
import { createItemFromDescriptor } from "./item";
import { buildRootChain, type ConfigContext } from "./config-chain";
import { getEnv } from "./helpers/environment";
import { validate, type ValidatedOptions } from "./validation/options";
import type { ConfigFile, IgnoreFile } from "./files";
export default function loadPrivatePartialConfig(
inputOpts: mixed,
): {
options: ValidatedOptions,
context: ConfigContext,
ignore: IgnoreFile | void,
babelrc: ConfigFile | void,
config: ConfigFile | void,
} | null {
if (
inputOpts != null &&
(typeof inputOpts !== "object" || Array.isArray(inputOpts))
) {
throw new Error("Babel options must be an object, null, or undefined");
}
const args = inputOpts ? validate("arguments", inputOpts) : {};
const { envName = getEnv(), cwd = ".", root: rootDir = "." } = args;
const absoluteCwd = path.resolve(cwd);
const absoluteRootDir = path.resolve(absoluteCwd, rootDir);
const context: ConfigContext = {
filename:
typeof args.filename === "string"
? path.resolve(cwd, args.filename)
: undefined,
cwd: absoluteCwd,
root: absoluteRootDir,
envName,
};
const configChain = buildRootChain(args, context);
if (!configChain) return null;
const options = {};
configChain.options.forEach(opts => {
mergeOptions(options, opts);
});
// Tack the passes onto the object itself so that, if this object is
// passed back to Babel a second time, it will be in the right structure
// to not change behavior.
options.babelrc = false;
options.configFile = false;
options.passPerPreset = false;
options.envName = context.envName;
options.cwd = context.cwd;
options.root = context.root;
options.filename =
typeof context.filename === "string"
? path.relative(context.cwd, context.filename)
: undefined;
options.plugins = configChain.plugins.map(descriptor =>
createItemFromDescriptor(descriptor),
);
options.presets = configChain.presets.map(descriptor =>
createItemFromDescriptor(descriptor),
);
return {
options,
context,
ignore: configChain.ignore,
babelrc: configChain.babelrc,
config: configChain.config,
};
}
export function loadPartialConfig(inputOpts: mixed): PartialConfig | null {
const result = loadPrivatePartialConfig(inputOpts);
if (!result) return null;
const { options, babelrc, ignore, config } = result;
(options.plugins || []).forEach(item => {
if (item.value instanceof Plugin) {
throw new Error(
"Passing cached plugin instances is not supported in " +
"babel.loadPartialConfig()",
);
}
});
return new PartialConfig(
options,
babelrc ? babelrc.filepath : undefined,
ignore ? ignore.filepath : undefined,
config ? config.filepath : undefined,
);
}
export type { PartialConfig };
class PartialConfig {
/**
* These properties are public, so any changes to them should be considered
* a breaking change to Babel's API.
*/
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.
Object.freeze(this);
}
/**
* Returns true if their is a config file in the filesystem for this config.
*
* While this only means .babelrc(.mjs)?/package.json#babel right now, it
* may well expand in the future, so using this is recommended vs checking
* this.babelrc directly.
*/
hasFilesystemConfig(): boolean {
return this.babelrc !== undefined || this.config !== undefined;
}
}
Object.freeze(PartialConfig.prototype);