UNPKG

5.37 kBJavaScriptView Raw
1'use strict'
2
3let clone = require('clone')
4let debug = require('debug')
5let empty = require('is-empty')
6let fs = require('mz/fs')
7let glob = require('globby')
8let merge = require('merge-array')
9let path = require('path')
10let Promise = require('bluebird')
11let resolve = require('resolve').sync
12let utils = require('mako-utils')
13
14// single export
15exports = module.exports = Promise.coroutine(load)
16exports.sync = loadSync
17
18/**
19 * Loads a mako configuration file.
20 *
21 * @param {String} file The absolute pathname to the config file to load.
22 * @param {Array} [overrides] User-supplied entries. (overrides config)
23 * @return {Object}
24 */
25function * load (file, overrides) {
26 let relative = utils.relative(file)
27 debug('loading %s', relative)
28
29 let exists = yield fs.exists(file)
30 if (!exists) throw new Error(`no mako config file located at ${relative}`)
31
32 let contents = yield fs.readFile(file, 'utf8')
33 let config = json(contents)
34 if (!config) throw new Error(`invalid JSON in mako config file located at ${relative}`)
35
36 let original = clone(config)
37 let dir = path.dirname(file)
38 let env = process.env.NODE_ENV || 'development'
39
40 if (env in config) {
41 debug('merging env %s', env)
42 let envConf = config[env]
43 if (envConf.entries) merge(config.entries, envConf.entries)
44 if (envConf.plugins) merge(config.plugins, envConf.plugins)
45 }
46
47 let result = {
48 path: file,
49 original: original,
50 entries: yield normalizeEntries(overrides, config.entries, dir),
51 plugins: normalizePlugins(config.plugins, dir)
52 }
53 if (config.root) result.root = path.resolve(dir, config.root)
54 if (config.concurrency) result.concurrency = config.concurrency
55 debug('final config: %j', result)
56 return result
57}
58
59/**
60 * Synchronous version of load.
61 *
62 * @param {String} file The absolute pathname to the config file to load.
63 * @param {Array} [overrides] User-supplied entries. (overrides config)
64 * @return {Object}
65 */
66function loadSync (file, overrides) {
67 let relative = utils.relative(file)
68 debug('loading %s (sync)', relative)
69
70 let exists = fs.existsSync(file)
71 if (!exists) throw new Error(`no mako config file located at ${relative}`)
72
73 let contents = fs.readFileSync(file, 'utf8')
74 let config = json(contents)
75 if (!config) throw new Error(`invalid JSON in mako config file located at ${relative}`)
76
77 let original = clone(config)
78 let dir = path.dirname(file)
79 let env = process.env.NODE_ENV || 'development'
80
81 if (env in config) {
82 debug('merging env %s', env)
83 let envConf = config[env]
84 if (envConf.entries) merge(config.entries, envConf.entries)
85 if (envConf.plugins) merge(config.plugins, envConf.plugins)
86 }
87
88 let result = {
89 path: file,
90 original: original,
91 entries: normalizeEntriesSync(overrides, config.entries, dir),
92 plugins: normalizePlugins(config.plugins, dir)
93 }
94 if (config.root) result.root = path.resolve(dir, config.root)
95 if (config.concurrency) result.concurrency = config.concurrency
96 debug('final config: %j', result)
97 return result
98}
99
100/**
101 * Normalizes the list of entries. By default, the config file will contain a
102 * complete list of entries. If the user passes a list of entries via the CLI,
103 * that list will be used instead.
104 *
105 * It will return a flattened array of absolute file paths, with any globs
106 * already expanded.
107 *
108 * @param {Array} input The input list from the user. (if specified)
109 * @param {Array} config The list specified in the config file.
110 * @param {String} dir The location of the config file.
111 * @return {Array}
112 */
113function normalizeEntries (input, config, dir) {
114 let list = !empty(input) ? input : config
115 return glob(list, { cwd: dir, realpath: true })
116}
117
118/**
119 * Synchronous version of normalizeEntries.
120 *
121 * @param {Array} input The input list from the user. (if specified)
122 * @param {Array} config The list specified in the config file.
123 * @param {String} dir The location of the config file.
124 * @return {Array}
125 */
126function normalizeEntriesSync (input, config, dir) {
127 let list = !empty(input) ? input : config
128 return glob.sync(list, { cwd: dir, realpath: true })
129}
130
131/**
132 * Normalizes the list of plugins. For each plugin specified in the config,
133 * it will initialize. (with arguments if supplied) The return value is a flat
134 * list of plugin functions.
135 *
136 * When a plugin is specified only as a string, it will be initialized without
137 * arguments. (basically accepting the defaults)
138 *
139 * When a plugin is specified as an array, the first item is the plugin name,
140 * all other items will be forwarded as arguments.
141 *
142 * @param {Array} config The list of plugins specified in the config.
143 * @param {String} root The location of the config file.
144 * @return {Array}
145 */
146function normalizePlugins (config, root) {
147 if (!config) return []
148 return config
149 .map(plugin => {
150 if (typeof plugin === 'string') return [ plugin ]
151 return clone(plugin)
152 })
153 .map(plugin => {
154 let fn = require(resolve(plugin[0], { basedir: root }))
155 return fn.apply(null, plugin.slice(1))
156 })
157}
158
159/**
160 * Helper for parsing file contents into an object.
161 *
162 * TODO: add validation for the required properties here.
163 *
164 * @param {String} str The JSON data to parse.
165 * @return {Object} config
166 */
167function json (str) {
168 try {
169 return JSON.parse(str)
170 } catch (err) {
171 return false
172 }
173}