• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

leizongmin / node-project-core / #28

28 Nov 2016 07:25AM UTC coverage: 88.5%. Remained the same
#28

push

leizongmin
fix $.event.emit()

59 of 83 branches covered (0.0%)

177 of 200 relevant lines covered (88.5%)

17.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.2
/lib/index.js
1
'use strict';
2

3
/**
4
 * project-core
5
 *
6
 * @author Zongmin Lei <leizongmin@gmail.com>
7
 */
8

9
const path = require('path');
1✔
10
const fs = require('fs');
1✔
11
const { EventEmitter } = require('events');
1✔
12
const { Namespace } = require('lei-ns');
1✔
13
const rd = require('rd');
1✔
14
const yaml = require('js-yaml');
1✔
15
const utils = require('./utils');
1✔
16
const debug = utils.debug('core');
1✔
17

18
module.exports = class ProjectCore {
1✔
19

20
  constructor() {
21
    this.utils = utils.extends();
17✔
22

23
    this._event = new EventEmitter();
17✔
24
    this._event.setMaxListeners(0);
17✔
25
    this.event = {};
17✔
26
    this.event.on = (e, fn) => this._event.on(e, utils.wrapFn(fn, this));
17✔
27
    this.event.once = (e, fn) => this._event.once(e, utils.wrapFn(fn, this));
18✔
28
    this.event.emit = (e, ...args) => {
17✔
29
      this._event.emit(e, ...args);
11✔
30
    };
31
    this._event.on('error', err => {
17✔
32
      if (this._event._events.error.length < 2) {
2!
33
        console.error(err.stack || err);
2!
34
      }
35
    });
36

37
    this.init._queue = [];
17✔
38
    this.init.add = (fn) => {
17✔
39
      this._checkIniting();
24✔
40
      this._checkInited();
24✔
41
      this.init._queue.push(this._wrapTask(fn));
23✔
42
    };
43
    this.init.load = (f) => {
17✔
44
      this._checkIniting();
2✔
45
      this._checkInited();
2✔
46
      const list = this._loadFileOrDirectory(f);
2✔
47
      for (const fn of list) {
2✔
48
        this.init.add(fn);
4✔
49
      }
50
    };
51

52
    this.config = new Namespace();
17✔
53
    this.config.load = file => {
17✔
54
      debug('config.load: %s', file);
5✔
55
      try {
5✔
56
        const fullPath = path.resolve(file);
5✔
57
        const ext = path.extname(fullPath).toLowerCase();
5✔
58
        if (ext === '.yaml' || ext === '.yml') {
5✔
59
          this.config.merge(yaml.safeLoad(fs.readFileSync(fullPath).toString()));
2✔
60
        } else if (ext === '.json') {
3✔
61
          this.config.merge(require(fullPath));
1✔
62
        } else if (ext === '.js' || ext === '') {
2!
63
          const initConfig = require(fullPath);
2✔
64
          if (typeof initConfig !== 'function') {
2!
65
            throw new Error(`incorrect config file format in file "${ file }"`);
×
66
          }
67
          const args = [
2✔
68
            (n, v) => this.config.set(n, v),
2✔
69
            (n) => this.config.get(n),
×
70
            (n) => this.config.has(n),
×
71
            this.config,
72
          ];
73
          initConfig.call(this.config, ...args);
2✔
74
        }
75
      } catch (err) {
76
        throw new Error(`failed to load config file "${ file }": ${ err.message }`);
×
77
      }
78
    };
79
    this.config._get = this.config.get;
17✔
80
    this.config.get = (n) => {
17✔
81
      const v = this.config._get(n);
1✔
82
      if (v === undefined) {
1!
83
        throw new TypeError(`config field "${ n }" is undefined`);
×
84
      }
85
      return v;
1✔
86
    };
87

88
    this._extends = {
17✔
89
      before: [],
90
      init: [],
91
      after: [],
92
    };
93

94
    this.inited = false;
17✔
95
    this.event.once('ready', () => {
17✔
96
      this.inited = true;
6✔
97
    });
98
  }
99

100
  _wrapTask(fn) {
101
    fn.__sourceLine = utils.getCallerSourceLine();
35✔
102
    debug('wrap: at %s', fn.__sourceLine);
35✔
103
    return fn;
35✔
104
  }
105

106
  _loadFile(f) {
107
    const m = require(f);
12✔
108
    let ret;
12✔
109
    if (typeof m === 'function') ret = m;
12!
110
    else if (typeof m.default === 'function') ret = m.default;
×
111
    else throw new Error(`module "${ f }" must export as a function`);
×
112
    ret.level = m.level || ret.level || 0;
12✔
113
    return this._wrapTask(ret);
12✔
114
  }
115

116
  _loadFileOrDirectory(f) {
117
    const s = fs.statSync(f);
6✔
118
    if (s.isFile()) {
6✔
119
      debug('load: %s', f);
3✔
120
      return [ this._loadFile(f) ];
3✔
121
    } else if (s.isDirectory()) {
3!
122
      return rd
3✔
123
              .readFileFilterSync(f, /\.js$/)
124
              .map(f => {
125
                debug('load: %s', f);
9✔
126
                return this._loadFile(f);
9✔
127
              })
128
              .sort((a, b) => b.level - a.level);
6✔
129
    }
130
    throw new Error(`"${ f }" is not a file or directory`);
×
131
  }
132

133
  _checkInited() {
134
    if (this.inited) {
39✔
135
      throw new Error('you cannot change project-core after it has been inited');
3✔
136
    }
137
  }
138

139
  _checkIniting() {
140
    if (this.initing) {
26!
141
      throw new Error('you cannot change project-core while it is initing');
×
142
    }
143
  }
144

145
  extends(info) {
146
    this._checkInited();
4✔
147

148
    if (typeof info.before === 'function') {
3✔
149
      this._extends.before.push(info.before);
2✔
150
    }
151
    if (typeof info.init === 'function') {
3!
152
      this._extends.init.push(info.init);
3✔
153
    }
154
    if (typeof info.after === 'function') {
3✔
155
      this._extends.after.push(info.after);
2✔
156
    }
157
  }
158

159
  init(...params) {
160
    this.initing = true;
9✔
161
    this._checkInited();
9✔
162
    debug('initing');
8✔
163

164
    const callback = params.pop();
8✔
165
    const cb = err => {
8✔
166
      if (callback) {
8!
167
        callback.call(this, err);
8✔
168
      }
169
    };
170

171
    this._extends = this._extends.before.concat(this._extends.init, this._extends.after);
8✔
172
    utils.runSeries(this._extends, this, params, err => {
8✔
173
      if (err) {
8✔
174
        this.event.emit('error', err);
1✔
175
        return cb(err);
1✔
176
      }
177

178
      utils.runSeries(this.init._queue, this, params, err => {
7✔
179
        if (err) {
7✔
180
          this.event.emit('error', err);
1✔
181
          return cb(err);
1✔
182
        }
183

184
        this.initing = false;
6✔
185
        this.event.emit('ready');
6✔
186
        cb(null);
6✔
187
      });
188
    });
189
  }
190

191
  ready(callback) {
192
    if (this.inited) {
3✔
193
      process.nextTick(callback);
2✔
194
    } else {
195
      this.event.once('ready', callback);
1✔
196
    }
197
  }
198

199
  run(tasks, ...params) {
200
    const callback = params.pop();
10✔
201
    const cb = err => {
10✔
202
      if (typeof callback === 'function') {
10!
203
        callback(err);
10✔
204
      } else if (err) {
×
205
        this.emit('error', err);
×
206
      }
207
    };
208
    const runTasks = list => {
10✔
209
      utils.runSeries(list, this, params, cb);
10✔
210
    };
211
    if (typeof tasks === 'function') {
10✔
212
      runTasks([ tasks ]);
6✔
213
    } else {
214
      runTasks(this._loadFileOrDirectory(tasks));
4✔
215
    }
216
  }
217

218
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc