rewrite options handling to be MUCH more maintainable and less like spaghetti

This commit is contained in:
Sebastian McKenzie 2015-07-09 21:11:51 +01:00
parent 9805c0387f
commit 9efb02f60f
5 changed files with 177 additions and 124 deletions

View File

@ -3,8 +3,8 @@ import merge from "lodash/object/merge";
export default function (dest, src) {
if (!dest || !src) return;
return merge(dest, src, function(a, b) {
if (Array.isArray(a)) {
return merge(dest, src, function (a, b) {
if (b && Array.isArray(a)) {
var c = a.slice(0);
for (var v of b) {
if (a.indexOf(v) < 0) {

View File

@ -1,25 +1,20 @@
import { validateOption, normaliseOptions, config as optionsConfig } from "./options";
import convertSourceMap from "convert-source-map";
import moduleFormatters from "../modules";
import OptionManager from "./options/option-manager";
import PluginManager from "./plugin-manager";
import shebangRegex from "shebang-regex";
import NodePath from "../../traversal/path";
import isFunction from "lodash/lang/isFunction";
import isAbsolute from "path-is-absolute";
import resolveRc from "./options/resolve-rc";
import sourceMap from "source-map";
import generate from "../../generation";
import codeFrame from "../../helpers/code-frame";
import defaults from "lodash/object/defaults";
import includes from "lodash/collection/includes";
import traverse from "../../traversal";
import assign from "lodash/object/assign";
import Logger from "./logger";
import Plugin from "../plugin";
import parse from "../../helpers/parse";
import merge from "../../helpers/merge";
import slash from "slash";
import clone from "lodash/lang/clone";
import Hub from "../../traversal/hub";
import * as util from "../../util";
import path from "path";
@ -50,10 +45,9 @@ export default class File {
this.pipeline = pipeline;
this.log = new Logger(this, opts.filename || "unknown");
this.opts = this.initOptions(opts);
this.ast = {};
this.normaliseOptions(opts);
this.buildTransformers();
this.hub = new Hub(this);
@ -98,56 +92,8 @@ export default class File {
static soloHelpers = [];
static options = optionsConfig;
normaliseOptions(opts: Object) {
opts = this.opts = normaliseOptions(assign({}, opts));
// resolve babelrc
if (opts.filename) {
var rcFilename = opts.filename;
if (!isAbsolute(rcFilename)) rcFilename = path.join(process.cwd(), rcFilename);
opts = resolveRc(rcFilename, opts);
}
// check for unknown options
for (let key in opts) {
if (key[0] === "_") continue;
let option = File.options[key];
if (!option) this.log.error(`Unknown option: ${key}`, ReferenceError);
}
// merge in environment options
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
if (opts.env) merge(opts, normaliseOptions(opts.env[envKey]));
// normalise options
for (let key in File.options) {
let option = File.options[key];
var val = opts[key];
// optional
if (!val && option.optional) continue;
// deprecated
if (val && option.deprecated) {
this.log.deprecate("Deprecated option " + key + ": " + option.deprecated);
}
// default
if (val == null) val = clone(option.default);
// validate
if (val) val = validateOption(key, val, this.pipeline);
// aaliases
if (option.alias) {
opts[option.alias] = opts[option.alias] || val;
} else {
opts[key] = val;
}
}
initOptions(opts) {
opts = new OptionManager(this.log, this.pipeline).init(opts);
if (opts.inputSourceMap) {
opts.sourceMaps = true;
@ -195,7 +141,7 @@ export default class File {
}
return opts;
};
}
isLoose(key: string) {
return includes(this.opts.loose, key);

View File

@ -253,7 +253,7 @@
},
"babelrc": {
"hidden": true,
"description": "do not load the same .babelrc file twice"
"description": "Specify a custom list of babelrc files to use",
"type": "list"
}
}

View File

@ -0,0 +1,168 @@
import stripJsonComments from "strip-json-comments";
import { validateOption, normaliseOptions } from "./index";
import isAbsolute from "path-is-absolute";
import clone from "lodash/lang/clone";
import merge from "../../../helpers/merge";
import config from "./config";
import path from "path";
import fs from "fs";
import pathExists from "path-exists";
var existsCache = {};
var jsonCache = {};
const CONFIG_FILENAME = ".babelrc";
function exists(filename) {
var cached = existsCache[filename];
if (cached != null) {
return cached;
} else {
return existsCache[filename] = pathExists.sync(filename);
}
}
export default class OptionManager {
constructor(log, pipeline) {
this.resolvedConfigs = [];
this.options = OptionManager.createBareOptions();
this.pipeline = pipeline;
this.log = log;
}
/**
* Description
*/
static createBareOptions() {
var opts = {};
for (var key in config) {
var opt = config[key];
opts[key] = clone(opt.default);
}
return opts;
}
/**
* Description
*/
addConfig(loc) {
if (this.resolvedConfigs.indexOf(loc) >= 0) return;
var content = fs.readFileSync(loc, "utf8");
var opts;
try {
opts = jsonCache[content] = jsonCache[content] || JSON.parse(stripJsonComments(content));
} catch (err) {
err.message = `${loc}: ${err.message}`;
throw err;
}
this.mergeOptions(opts, loc);
this.resolvedConfigs.push(loc);
}
/**
* Description
*/
mergeOptions(opts, alias) {
if (!opts) return;
for (let key in opts) {
if (key[0] === "_") continue;
let option = config[key];
// check for an unknown option
if (!option) this.log.error(`Unknown option: ${alias}.${key}`, ReferenceError);
}
// normalise options
normaliseOptions(opts);
// merge them into this current files options
merge(this.options, opts);
}
/**
* Description
*/
findConfigs(loc) {
if (!loc) return;
if (!isAbsolute(loc)) {
loc = path.join(process.cwd(), loc);
}
while (loc !== (loc = path.dirname(loc))) {
if (this.options.breakConfig) return;
var configLoc = path.join(loc, CONFIG_FILENAME);
if (exists(configLoc)) this.addConfig(configLoc);
}
}
/**
* Description
*/
normaliseOptions() {
var opts = this.options;
for (let key in config) {
var option = config[key];
var val = opts[key];
// optional
if (!val && option.optional) continue;
// deprecated
if (val && option.deprecated) {
this.log.deprecate(`Deprecated option ${key}: ${option.deprecated}`);
}
// validate
if (val) val = validateOption(key, val, this.pipeline);
// aaliases
if (option.alias) {
opts[option.alias] = opts[option.alias] || val;
} else {
opts[key] = val;
}
}
}
/**
* Description
*/
init(opts) {
this.mergeOptions(opts, "direct");
// babelrc option
if (opts.babelrc) {
for (var loc of (opts.babelrc: Array)) this.addConfig(loc);
}
// resolve all .babelrc files
this.findConfigs(opts.filename);
// merge in env
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
if (this.options.env) {
this.mergeOptions(this.options.env[envKey], `direct.env.${envKey}`);
}
// normalise
this.normaliseOptions(opts);
return this.options;
}
}

View File

@ -1,61 +0,0 @@
import stripJsonComments from "strip-json-comments";
import { normaliseOptions } from "./index";
import merge from "../../../helpers/merge";
import path from "path";
import fs from "fs";
import pathExists from "path-exists";
var cache = {};
var jsons = {};
function exists(filename) {
var cached = cache[filename];
if (cached != null) return cached;
return cache[filename] = pathExists.sync(filename);
}
export default function (loc, opts = {}) {
var rel = ".babelrc";
if (!opts.babelrc) {
opts.babelrc = [];
}
function find(start, rel) {
var file = path.join(start, rel);
if (opts.babelrc.indexOf(file) >= 0) {
return;
}
if (exists(file)) {
var content = fs.readFileSync(file, "utf8");
var json;
try {
json = jsons[content] = jsons[content] || JSON.parse(stripJsonComments(content));
normaliseOptions(json);
} catch (err) {
err.message = `${file}: ${err.message}`;
throw err;
}
opts.babelrc.push(file);
if (json.breakConfig) return;
merge(opts, json);
}
var up = path.dirname(start);
if (up !== start) { // root
find(up, rel);
}
}
if (opts.babelrc.indexOf(loc) < 0 && opts.breakConfig !== true) {
find(loc, rel);
}
return opts;
}