forked from millermedeiros/esformatter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoptions.js
165 lines (128 loc) · 4.57 KB
/
options.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"use strict";
var stripJsonComments = require('strip-json-comments');
var fs = require('fs');
var path = require('path');
var _ws = require('rocambole-whitespace');
var _br = require('rocambole-linebreak');
var indent = require('./indent');
var plugins = require('./plugins');
var deepMixIn = require('mout/object/deepMixIn');
var merge = require('mout/object/merge');
var get = require('mout/object/get');
var isObject = require('mout/lang/isObject');
// ---
var _curOpts;
// ---
exports.presets = {
'default': require('./preset/default.json'),
'jquery' : require('./preset/jquery.json')
};
exports.set = function(opts) {
var preset = opts && opts.preset ? opts.preset : 'default';
// we need to pass all the user settings and default settings to the plugins
// so they are able to toggle the behavior and make changes based on the
// options
_curOpts = mergeOptions(preset, opts);
_ws.setOptions(_curOpts.whiteSpace);
_br.setOptions(_curOpts.lineBreak);
indent.setOptions(_curOpts.indent);
plugins.setOptions(_curOpts);
// user provided options should override default settings and also any
// changes made by plugins
if (opts) {
_curOpts = deepMixIn(_curOpts, opts);
}
};
function mergeOptions(preset, opts){
if (!(preset in exports.presets)) {
throw new Error('Invalid preset file "' + preset + '".');
}
var baseOpts = exports.presets[preset];
// recursively merge options to allow a "prototype chain"
if (baseOpts.preset) {
baseOpts = mergeOptions(baseOpts.preset, baseOpts);
}
return merge({}, baseOpts, opts);
}
exports.get = function(prop) {
return prop ? get(_curOpts, prop) : _curOpts;
};
exports.getRc = getRc;
function getRc(filePath, customOptions) {
// if user sets the "preset" we don't load any other config file
// we assume the "preset" overrides any user settings
if (isTopLevel(customOptions)) {
return customOptions;
}
if (isObject(filePath)) {
customOptions = filePath;
filePath = null;
}
// we search for config file starting from source directory or from cwd if
// path is not provided
var basedir = filePath ? path.dirname(filePath) : process.cwd();
var cwd = process.cwd();
var rc = findAndMergeConfigs(basedir);
if (!rc && basedir !== cwd) {
rc = findAndMergeConfigs(cwd);
}
return merge(rc || getGlobalConfig(), customOptions);
}
function findAndMergeConfigs(basedir) {
if (!basedir || !basedir.length) return;
var configFiles = ['.esformatter', 'package.json'];
var config;
configFiles.some(function(name) {
var filePath = path.join(basedir, name);
if (!fs.existsSync(filePath)) return;
var cur = loadAndParseConfig(filePath);
if (name === 'package.json') {
cur = cur.esformatter;
}
if (!cur) return;
// we merge configs on same folder as well just in case user have
// ".esformatter" and "package.json" on same folder
// notice that ".esformatter" file takes precedence and will override the
// "package.json" settings.
config = config ? merge(cur, config) : cur;
// stop the loop
if (isTopLevel(config)) return true;
});
if (isTopLevel(config)) {
return config;
}
// we merge configs from parent folders so it's easier to add different rules
// for each folder on a project and/or override just specific settings
var parentDir = path.resolve(basedir, '..');
// we need to check if parentDir is different from basedir to avoid conflicts
// on windows (see #174)
var parentConfig = parentDir && parentDir !== basedir ?
findAndMergeConfigs(parentDir) :
{};
// notice that current folder config overrides the parent folder config
return merge(parentConfig, config);
}
function isTopLevel(config) {
// if config contains 'root:true' or inherit from another "preset" we
// consider it as top-level and don't merge the settings with config files on
// parent folders.
return config && (config.root || config.preset);
}
function getGlobalConfig() {
var home = process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME;
var file = path.join(home, '.esformatter');
return fs.existsSync(file) ? loadAndParseConfig(file) : {};
}
exports.loadAndParseConfig = loadAndParseConfig;
function loadAndParseConfig(file) {
try {
return JSON.parse(stripJsonComments(fs.readFileSync(file).toString()));
} catch (e) {
// include file name and let user know error was caused by config file
// parsing. this is redundant for ENOENT errors but very helpful for
// JSON.parse
throw new Error(
"Can't parse configuration file '" + file + "'. Exception: " + e.message
);
}
}