1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | 'use strict';
|
8 |
|
9 | const assert = require('bsert');
|
10 | const Path = require('path');
|
11 | const os = require('os');
|
12 | const fs = require('./fs');
|
13 | const HOME = os.homedir ? os.homedir() : '/';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | class Config {
|
20 | |
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | constructor(module, options) {
|
28 | assert(typeof module === 'string');
|
29 | assert(module.length > 0);
|
30 |
|
31 | this.module = module;
|
32 | this.prefix = Path.join(HOME, `.${module}`);
|
33 | this.suffix = null;
|
34 | this.fallback = null;
|
35 | this.alias = Object.create(null);
|
36 |
|
37 | this.options = Object.create(null);
|
38 | this.data = Object.create(null);
|
39 | this.env = Object.create(null);
|
40 | this.args = Object.create(null);
|
41 | this.argv = [];
|
42 | this.pass = [];
|
43 | this.query = Object.create(null);
|
44 | this.hash = Object.create(null);
|
45 |
|
46 | if (options)
|
47 | this.init(options);
|
48 | }
|
49 |
|
50 | |
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | init(options) {
|
57 | assert(options && typeof options === 'object');
|
58 |
|
59 | if (options.suffix != null) {
|
60 | assert(typeof options.suffix === 'string');
|
61 | this.suffix = options.suffix;
|
62 | }
|
63 |
|
64 | if (options.fallback != null) {
|
65 | assert(typeof options.fallback === 'string');
|
66 | this.fallback = options.fallback;
|
67 | }
|
68 |
|
69 | if (options.alias) {
|
70 | assert(typeof options.alias === 'object');
|
71 | for (const key of Object.keys(options.alias)) {
|
72 | const value = options.alias[key];
|
73 | assert(typeof value === 'string');
|
74 | this.alias[key] = value;
|
75 | }
|
76 | }
|
77 | }
|
78 |
|
79 | |
80 |
|
81 |
|
82 |
|
83 |
|
84 | inject(options) {
|
85 | for (const key of Object.keys(options)) {
|
86 | const value = options[key];
|
87 |
|
88 | switch (key) {
|
89 | case 'hash':
|
90 | case 'query':
|
91 | case 'env':
|
92 | case 'argv':
|
93 | case 'config':
|
94 | continue;
|
95 | }
|
96 |
|
97 | this.set(key, value);
|
98 | }
|
99 | }
|
100 |
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 | load(options) {
|
107 | if (options.hash)
|
108 | this.parseHash(options.hash);
|
109 |
|
110 | if (options.query)
|
111 | this.parseQuery(options.query);
|
112 |
|
113 | if (options.env)
|
114 | this.parseEnv(options.env);
|
115 |
|
116 | if (options.argv)
|
117 | this.parseArg(options.argv);
|
118 |
|
119 | this.prefix = this.getPrefix();
|
120 | }
|
121 |
|
122 | |
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | open(file) {
|
129 | if (fs.unsupported)
|
130 | return;
|
131 |
|
132 | const path = this.getFile(file);
|
133 |
|
134 | let text;
|
135 | try {
|
136 | text = fs.readFileSync(path, 'utf8');
|
137 | } catch (e) {
|
138 | if (e.code === 'ENOENT')
|
139 | return;
|
140 | throw e;
|
141 | }
|
142 |
|
143 | this.parseConfig(text);
|
144 |
|
145 | this.prefix = this.getPrefix();
|
146 | }
|
147 |
|
148 | |
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | filter(name) {
|
155 | assert(typeof name === 'string');
|
156 |
|
157 | const child = new Config(this.module);
|
158 |
|
159 | child.prefix = this.prefix;
|
160 | child.suffix = this.suffix;
|
161 | child.fallback = this.fallback;
|
162 | child.argv = this.argv;
|
163 | child.pass = this.pass;
|
164 |
|
165 | _filter(name, this.env, child.env);
|
166 | _filter(name, this.args, child.args);
|
167 | _filter(name, this.query, child.query);
|
168 | _filter(name, this.hash, child.hash);
|
169 |
|
170 | return child;
|
171 | }
|
172 |
|
173 | |
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | set(key, value) {
|
180 | assert(typeof key === 'string', 'Key must be a string.');
|
181 |
|
182 | if (value == null)
|
183 | return;
|
184 |
|
185 | key = key.replace(/-/g, '');
|
186 | key = key.toLowerCase();
|
187 |
|
188 | this.options[key] = value;
|
189 | }
|
190 |
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | has(key) {
|
198 | if (typeof key === 'number') {
|
199 | assert(key >= 0, 'Index must be positive.');
|
200 | if (key >= this.argv.length)
|
201 | return false;
|
202 | return true;
|
203 | }
|
204 |
|
205 | assert(typeof key === 'string', 'Key must be a string.');
|
206 |
|
207 | key = key.replace(/-/g, '');
|
208 | key = key.toLowerCase();
|
209 |
|
210 | if (this.hash[key] != null)
|
211 | return true;
|
212 |
|
213 | if (this.query[key] != null)
|
214 | return true;
|
215 |
|
216 | if (this.args[key] != null)
|
217 | return true;
|
218 |
|
219 | if (this.env[key] != null)
|
220 | return true;
|
221 |
|
222 | if (this.data[key] != null)
|
223 | return true;
|
224 |
|
225 | if (this.options[key] != null)
|
226 | return true;
|
227 |
|
228 | return false;
|
229 | }
|
230 |
|
231 | |
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | get(key, fallback) {
|
239 | if (fallback === undefined)
|
240 | fallback = null;
|
241 |
|
242 | if (Array.isArray(key)) {
|
243 | const keys = key;
|
244 | for (const key of keys) {
|
245 | const value = this.get(key);
|
246 | if (value !== null)
|
247 | return value;
|
248 | }
|
249 | return fallback;
|
250 | }
|
251 |
|
252 | if (typeof key === 'number') {
|
253 | assert(key >= 0, 'Index must be positive.');
|
254 |
|
255 | if (key >= this.argv.length)
|
256 | return fallback;
|
257 |
|
258 | if (this.argv[key] != null)
|
259 | return this.argv[key];
|
260 |
|
261 | return fallback;
|
262 | }
|
263 |
|
264 | assert(typeof key === 'string', 'Key must be a string.');
|
265 |
|
266 | key = key.replace(/-/g, '');
|
267 | key = key.toLowerCase();
|
268 |
|
269 | if (this.hash[key] != null)
|
270 | return this.hash[key];
|
271 |
|
272 | if (this.query[key] != null)
|
273 | return this.query[key];
|
274 |
|
275 | if (this.args[key] != null)
|
276 | return this.args[key];
|
277 |
|
278 | if (this.env[key] != null)
|
279 | return this.env[key];
|
280 |
|
281 | if (this.data[key] != null)
|
282 | return this.data[key];
|
283 |
|
284 | if (this.options[key] != null)
|
285 | return this.options[key];
|
286 |
|
287 | return fallback;
|
288 | }
|
289 |
|
290 | |
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 | typeOf(key) {
|
297 | const value = this.get(key);
|
298 |
|
299 | if (value === null)
|
300 | return 'null';
|
301 |
|
302 | return typeof value;
|
303 | }
|
304 |
|
305 | |
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 | str(key, fallback) {
|
313 | const value = this.get(key);
|
314 |
|
315 | if (fallback === undefined)
|
316 | fallback = null;
|
317 |
|
318 | if (value === null)
|
319 | return fallback;
|
320 |
|
321 | if (typeof value !== 'string')
|
322 | throw new Error(`${fmt(key)} must be a string.`);
|
323 |
|
324 | return value;
|
325 | }
|
326 |
|
327 | |
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | int(key, fallback) {
|
335 | const value = this.get(key);
|
336 |
|
337 | if (fallback === undefined)
|
338 | fallback = null;
|
339 |
|
340 | if (value === null)
|
341 | return fallback;
|
342 |
|
343 | if (typeof value !== 'string') {
|
344 | if (typeof value !== 'number')
|
345 | throw new Error(`${fmt(key)} must be an int.`);
|
346 |
|
347 | if (!Number.isSafeInteger(value))
|
348 | throw new Error(`${fmt(key)} must be an int.`);
|
349 |
|
350 | return value;
|
351 | }
|
352 |
|
353 | if (!/^\-?\d+$/.test(value))
|
354 | throw new Error(`${fmt(key)} must be an int.`);
|
355 |
|
356 | const num = parseInt(value, 10);
|
357 |
|
358 | if (!Number.isSafeInteger(num))
|
359 | throw new Error(`${fmt(key)} must be an int.`);
|
360 |
|
361 | return num;
|
362 | }
|
363 |
|
364 | |
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 | uint(key, fallback) {
|
372 | const value = this.int(key);
|
373 |
|
374 | if (fallback === undefined)
|
375 | fallback = null;
|
376 |
|
377 | if (value === null)
|
378 | return fallback;
|
379 |
|
380 | if (value < 0)
|
381 | throw new Error(`${fmt(key)} must be a uint.`);
|
382 |
|
383 | return value;
|
384 | }
|
385 |
|
386 | |
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | float(key, fallback) {
|
394 | const value = this.get(key);
|
395 |
|
396 | if (fallback === undefined)
|
397 | fallback = null;
|
398 |
|
399 | if (value === null)
|
400 | return fallback;
|
401 |
|
402 | if (typeof value !== 'string') {
|
403 | if (typeof value !== 'number')
|
404 | throw new Error(`${fmt(key)} must be a float.`);
|
405 |
|
406 | if (!isFinite(value))
|
407 | throw new Error(`${fmt(key)} must be a float.`);
|
408 |
|
409 | return value;
|
410 | }
|
411 |
|
412 | if (!/^\-?\d*(?:\.\d*)?$/.test(value))
|
413 | throw new Error(`${fmt(key)} must be a float.`);
|
414 |
|
415 | if (!/\d/.test(value))
|
416 | throw new Error(`${fmt(key)} must be a float.`);
|
417 |
|
418 | const num = parseFloat(value);
|
419 |
|
420 | if (!isFinite(num))
|
421 | throw new Error(`${fmt(key)} must be a float.`);
|
422 |
|
423 | return num;
|
424 | }
|
425 |
|
426 | |
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | ufloat(key, fallback) {
|
434 | const value = this.float(key);
|
435 |
|
436 | if (fallback === undefined)
|
437 | fallback = null;
|
438 |
|
439 | if (value === null)
|
440 | return fallback;
|
441 |
|
442 | if (value < 0)
|
443 | throw new Error(`${fmt(key)} must be a positive float.`);
|
444 |
|
445 | return value;
|
446 | }
|
447 |
|
448 | |
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 | fixed(key, exp, fallback) {
|
457 | const value = this.float(key);
|
458 |
|
459 | if (fallback === undefined)
|
460 | fallback = null;
|
461 |
|
462 | if (value === null)
|
463 | return fallback;
|
464 |
|
465 | try {
|
466 | return fromFloat(value, exp || 0);
|
467 | } catch (e) {
|
468 | throw new Error(`${fmt(key)} must be a fixed number.`);
|
469 | }
|
470 | }
|
471 |
|
472 | |
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 | ufixed(key, exp, fallback) {
|
481 | const value = this.fixed(key, exp);
|
482 |
|
483 | if (fallback === undefined)
|
484 | fallback = null;
|
485 |
|
486 | if (value === null)
|
487 | return fallback;
|
488 |
|
489 | if (value < 0)
|
490 | throw new Error(`${fmt(key)} must be a positive fixed number.`);
|
491 |
|
492 | return value;
|
493 | }
|
494 |
|
495 | |
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 | bool(key, fallback) {
|
503 | const value = this.get(key);
|
504 |
|
505 | if (fallback === undefined)
|
506 | fallback = null;
|
507 |
|
508 | if (value === null)
|
509 | return fallback;
|
510 |
|
511 |
|
512 | if (typeof value === 'number') {
|
513 | if (value === 1)
|
514 | return true;
|
515 |
|
516 | if (value === 0)
|
517 | return false;
|
518 | }
|
519 |
|
520 | if (typeof value !== 'string') {
|
521 | if (typeof value !== 'boolean')
|
522 | throw new Error(`${fmt(key)} must be a boolean.`);
|
523 | return value;
|
524 | }
|
525 |
|
526 | if (value === 'true' || value === '1')
|
527 | return true;
|
528 |
|
529 | if (value === 'false' || value === '0')
|
530 | return false;
|
531 |
|
532 | throw new Error(`${fmt(key)} must be a boolean.`);
|
533 | }
|
534 |
|
535 | |
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 | buf(key, fallback, enc) {
|
543 | const value = this.get(key);
|
544 |
|
545 | if (!enc)
|
546 | enc = 'hex';
|
547 |
|
548 | if (fallback === undefined)
|
549 | fallback = null;
|
550 |
|
551 | if (value === null)
|
552 | return fallback;
|
553 |
|
554 | if (typeof value !== 'string') {
|
555 | if (!Buffer.isBuffer(value))
|
556 | throw new Error(`${fmt(key)} must be a buffer.`);
|
557 | return value;
|
558 | }
|
559 |
|
560 | const data = Buffer.from(value, enc);
|
561 |
|
562 | if (data.length !== Buffer.byteLength(value, enc))
|
563 | throw new Error(`${fmt(key)} must be a ${enc} string.`);
|
564 |
|
565 | return data;
|
566 | }
|
567 |
|
568 | |
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 | array(key, fallback) {
|
576 | const value = this.get(key);
|
577 |
|
578 | if (fallback === undefined)
|
579 | fallback = null;
|
580 |
|
581 | if (value === null)
|
582 | return fallback;
|
583 |
|
584 | if (typeof value !== 'string') {
|
585 | if (!Array.isArray(value))
|
586 | throw new Error(`${fmt(key)} must be an array.`);
|
587 | return value;
|
588 | }
|
589 |
|
590 | const parts = value.trim().split(/\s*,\s*/);
|
591 | const result = [];
|
592 |
|
593 | for (const part of parts) {
|
594 | if (part.length === 0)
|
595 | continue;
|
596 |
|
597 | result.push(part);
|
598 | }
|
599 |
|
600 | return result;
|
601 | }
|
602 |
|
603 | |
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 | obj(key, fallback) {
|
611 | const value = this.get(key);
|
612 |
|
613 | if (fallback === undefined)
|
614 | fallback = null;
|
615 |
|
616 | if (value === null)
|
617 | return fallback;
|
618 |
|
619 | if (typeof value !== 'object' || Array.isArray(value))
|
620 | throw new Error(`${fmt(key)} must be an object.`);
|
621 |
|
622 | return value;
|
623 | }
|
624 |
|
625 | |
626 |
|
627 |
|
628 |
|
629 |
|
630 |
|
631 |
|
632 | func(key, fallback) {
|
633 | const value = this.get(key);
|
634 |
|
635 | if (fallback === undefined)
|
636 | fallback = null;
|
637 |
|
638 | if (value === null)
|
639 | return fallback;
|
640 |
|
641 | if (typeof value !== 'function')
|
642 | throw new Error(`${fmt(key)} must be a function.`);
|
643 |
|
644 | return value;
|
645 | }
|
646 |
|
647 | |
648 |
|
649 |
|
650 |
|
651 |
|
652 |
|
653 |
|
654 | path(key, fallback) {
|
655 | let value = this.str(key);
|
656 |
|
657 | if (fallback === undefined)
|
658 | fallback = null;
|
659 |
|
660 | if (value === null)
|
661 | return fallback;
|
662 |
|
663 | if (value.length === 0)
|
664 | return fallback;
|
665 |
|
666 | switch (value[0]) {
|
667 | case '~':
|
668 | value = Path.join(HOME, value.substring(1));
|
669 | break;
|
670 | case '@':
|
671 | value = Path.join(this.prefix, value.substring(1));
|
672 | break;
|
673 | default:
|
674 | break;
|
675 | }
|
676 |
|
677 | return Path.normalize(value);
|
678 | }
|
679 |
|
680 | |
681 |
|
682 |
|
683 |
|
684 |
|
685 |
|
686 |
|
687 | mb(key, fallback) {
|
688 | const value = this.uint(key);
|
689 |
|
690 | if (fallback === undefined)
|
691 | fallback = null;
|
692 |
|
693 | if (value === null)
|
694 | return fallback;
|
695 |
|
696 | return value * 1024 * 1024;
|
697 | }
|
698 |
|
699 | |
700 |
|
701 |
|
702 |
|
703 |
|
704 | getSuffix() {
|
705 | if (!this.suffix)
|
706 | throw new Error('No suffix presented.');
|
707 |
|
708 | const suffix = this.str(this.suffix, this.fallback);
|
709 |
|
710 | assert(isAlpha(suffix), 'Bad suffix.');
|
711 |
|
712 | return suffix;
|
713 | }
|
714 |
|
715 | |
716 |
|
717 |
|
718 |
|
719 |
|
720 |
|
721 | getPrefix() {
|
722 | let prefix = this.str('prefix');
|
723 |
|
724 | if (prefix) {
|
725 | if (prefix[0] === '~')
|
726 | prefix = Path.join(HOME, prefix.substring(1));
|
727 | } else {
|
728 | prefix = Path.join(HOME, `.${this.module}`);
|
729 | }
|
730 |
|
731 | if (this.suffix) {
|
732 | const suffix = this.str(this.suffix);
|
733 |
|
734 | if (suffix) {
|
735 | assert(isAlpha(suffix), 'Bad suffix.');
|
736 | if (this.fallback && suffix !== this.fallback)
|
737 | prefix = Path.join(prefix, suffix);
|
738 | }
|
739 | }
|
740 |
|
741 | return Path.normalize(prefix);
|
742 | }
|
743 |
|
744 | |
745 |
|
746 |
|
747 |
|
748 |
|
749 |
|
750 |
|
751 | getFile(file) {
|
752 | const name = this.str('config');
|
753 |
|
754 | if (name)
|
755 | return name;
|
756 |
|
757 | return Path.join(this.prefix, file);
|
758 | }
|
759 |
|
760 | |
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 | location(file) {
|
767 | return Path.join(this.prefix, file);
|
768 | }
|
769 |
|
770 | |
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 | parseConfig(text) {
|
777 | assert(typeof text === 'string', 'Config must be text.');
|
778 |
|
779 | if (text.charCodeAt(0) === 0xfeff)
|
780 | text = text.substring(1);
|
781 |
|
782 | text = text.replace(/\r\n/g, '\n');
|
783 | text = text.replace(/\r/g, '\n');
|
784 | text = text.replace(/\\\n/g, '');
|
785 |
|
786 | let num = 0;
|
787 |
|
788 | for (const chunk of text.split('\n')) {
|
789 | const line = chunk.trim();
|
790 |
|
791 | num += 1;
|
792 |
|
793 | if (line.length === 0)
|
794 | continue;
|
795 |
|
796 | if (line[0] === '#')
|
797 | continue;
|
798 |
|
799 | const index = line.indexOf(':');
|
800 |
|
801 | if (index === -1)
|
802 | throw new Error(`Expected ':' on line ${num}: "${line}".`);
|
803 |
|
804 | let key = line.substring(0, index).trim();
|
805 |
|
806 | key = key.replace(/\-/g, '');
|
807 |
|
808 | if (!isLowerKey(key))
|
809 | throw new Error(`Invalid option on line ${num}: ${key}.`);
|
810 |
|
811 | const value = line.substring(index + 1).trim();
|
812 |
|
813 | if (value.length === 0)
|
814 | continue;
|
815 |
|
816 | const alias = this.alias[key];
|
817 |
|
818 | if (alias)
|
819 | key = alias;
|
820 |
|
821 | this.data[key] = value;
|
822 | }
|
823 | }
|
824 |
|
825 | |
826 |
|
827 |
|
828 |
|
829 |
|
830 |
|
831 | parseArg(argv) {
|
832 | if (!argv || typeof argv !== 'object')
|
833 | argv = process.argv;
|
834 |
|
835 | assert(Array.isArray(argv));
|
836 |
|
837 | let last = null;
|
838 | let pass = false;
|
839 |
|
840 | for (let i = 2; i < argv.length; i++) {
|
841 | const arg = argv[i];
|
842 |
|
843 | assert(typeof arg === 'string');
|
844 |
|
845 | if (arg === '--') {
|
846 | pass = true;
|
847 | continue;
|
848 | }
|
849 |
|
850 | if (pass) {
|
851 | this.pass.push(arg);
|
852 | continue;
|
853 | }
|
854 |
|
855 | if (arg.length === 0) {
|
856 | last = null;
|
857 | continue;
|
858 | }
|
859 |
|
860 | if (arg.indexOf('--') === 0) {
|
861 | const index = arg.indexOf('=');
|
862 |
|
863 | let key = null;
|
864 | let value = null;
|
865 | let empty = false;
|
866 |
|
867 | if (index !== -1) {
|
868 |
|
869 | key = arg.substring(2, index);
|
870 | value = arg.substring(index + 1);
|
871 | last = null;
|
872 | empty = false;
|
873 | } else {
|
874 |
|
875 | key = arg.substring(2);
|
876 | value = 'true';
|
877 | last = null;
|
878 | empty = true;
|
879 | }
|
880 |
|
881 | key = key.replace(/\-/g, '');
|
882 |
|
883 | if (!isLowerKey(key))
|
884 | throw new Error(`Invalid argument: --${key}.`);
|
885 |
|
886 | if (value.length === 0)
|
887 | continue;
|
888 |
|
889 |
|
890 | if (key.length > 1) {
|
891 | const alias = this.alias[key];
|
892 | if (alias)
|
893 | key = alias;
|
894 | }
|
895 |
|
896 | this.args[key] = value;
|
897 |
|
898 | if (empty)
|
899 | last = key;
|
900 |
|
901 | continue;
|
902 | }
|
903 |
|
904 | if (arg[0] === '-') {
|
905 |
|
906 | last = null;
|
907 |
|
908 | for (let j = 1; j < arg.length; j++) {
|
909 | let key = arg[j];
|
910 |
|
911 | if ((key < 'a' || key > 'z')
|
912 | && (key < 'A' || key > 'Z')
|
913 | && (key < '0' || key > '9')
|
914 | && key !== '?') {
|
915 | throw new Error(`Invalid argument: -${key}.`);
|
916 | }
|
917 |
|
918 | const alias = this.alias[key];
|
919 |
|
920 | if (alias)
|
921 | key = alias;
|
922 |
|
923 | this.args[key] = 'true';
|
924 |
|
925 | last = key;
|
926 | }
|
927 |
|
928 | continue;
|
929 | }
|
930 |
|
931 |
|
932 | const value = arg;
|
933 |
|
934 | if (value.length === 0) {
|
935 | last = null;
|
936 | continue;
|
937 | }
|
938 |
|
939 | if (last) {
|
940 | this.args[last] = value;
|
941 | last = null;
|
942 | } else {
|
943 | this.argv.push(value);
|
944 | }
|
945 | }
|
946 | }
|
947 |
|
948 | |
949 |
|
950 |
|
951 |
|
952 |
|
953 |
|
954 |
|
955 | parseEnv(env) {
|
956 | let prefix = this.module;
|
957 |
|
958 | prefix = prefix.toUpperCase();
|
959 | prefix = prefix.replace(/-/g, '_');
|
960 | prefix += '_';
|
961 |
|
962 | if (!env || typeof env !== 'object')
|
963 | env = process.env;
|
964 |
|
965 | assert(env && typeof env === 'object');
|
966 |
|
967 | for (let key of Object.keys(env)) {
|
968 | const value = env[key];
|
969 |
|
970 | assert(typeof value === 'string');
|
971 |
|
972 | if (key.indexOf(prefix) !== 0)
|
973 | continue;
|
974 |
|
975 | key = key.substring(prefix.length);
|
976 | key = key.replace(/_/g, '');
|
977 |
|
978 | if (!isUpperKey(key))
|
979 | continue;
|
980 |
|
981 | if (value.length === 0)
|
982 | continue;
|
983 |
|
984 | key = key.toLowerCase();
|
985 |
|
986 |
|
987 | if (key.length > 1) {
|
988 | const alias = this.alias[key];
|
989 | if (alias)
|
990 | key = alias;
|
991 | }
|
992 |
|
993 | this.env[key] = value;
|
994 | }
|
995 | }
|
996 |
|
997 | |
998 |
|
999 |
|
1000 |
|
1001 |
|
1002 |
|
1003 | parseQuery(query) {
|
1004 | if (typeof query !== 'string') {
|
1005 | if (!global.location)
|
1006 | return {};
|
1007 |
|
1008 | query = global.location.search;
|
1009 |
|
1010 | if (typeof query !== 'string')
|
1011 | return {};
|
1012 | }
|
1013 |
|
1014 | return this.parseForm(query, '?', this.query);
|
1015 | }
|
1016 |
|
1017 | |
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 | parseHash(hash) {
|
1024 | if (typeof hash !== 'string') {
|
1025 | if (!global.location)
|
1026 | return {};
|
1027 |
|
1028 | hash = global.location.hash;
|
1029 |
|
1030 | if (typeof hash !== 'string')
|
1031 | return {};
|
1032 | }
|
1033 |
|
1034 | return this.parseForm(hash, '#', this.hash);
|
1035 | }
|
1036 |
|
1037 | |
1038 |
|
1039 |
|
1040 |
|
1041 |
|
1042 |
|
1043 |
|
1044 |
|
1045 | parseForm(query, ch, map) {
|
1046 | assert(typeof query === 'string');
|
1047 |
|
1048 | if (query.length === 0)
|
1049 | return;
|
1050 |
|
1051 | if (query[0] === ch)
|
1052 | query = query.substring(1);
|
1053 |
|
1054 | for (const pair of query.split('&')) {
|
1055 | const index = pair.indexOf('=');
|
1056 |
|
1057 | let key, value;
|
1058 | if (index !== -1) {
|
1059 | key = pair.substring(0, index);
|
1060 | value = pair.substring(index + 1);
|
1061 | } else {
|
1062 | key = pair;
|
1063 | value = 'true';
|
1064 | }
|
1065 |
|
1066 | key = unescape(key);
|
1067 | key = key.replace(/\-/g, '');
|
1068 |
|
1069 | if (!isLowerKey(key))
|
1070 | continue;
|
1071 |
|
1072 | value = unescape(value);
|
1073 |
|
1074 | if (value.length === 0)
|
1075 | continue;
|
1076 |
|
1077 | const alias = this.alias[key];
|
1078 |
|
1079 | if (alias)
|
1080 | key = alias;
|
1081 |
|
1082 | map[key] = value;
|
1083 | }
|
1084 | }
|
1085 | }
|
1086 |
|
1087 |
|
1088 |
|
1089 |
|
1090 |
|
1091 | function fmt(key) {
|
1092 | if (Array.isArray(key))
|
1093 | key = key[0];
|
1094 |
|
1095 | if (typeof key === 'number')
|
1096 | return `Argument #${key}`;
|
1097 |
|
1098 | return key;
|
1099 | }
|
1100 |
|
1101 | function unescape(str) {
|
1102 | try {
|
1103 | str = decodeURIComponent(str);
|
1104 | str = str.replace(/\+/g, ' ');
|
1105 | } catch (e) {
|
1106 | ;
|
1107 | }
|
1108 | str = str.replace(/\0/g, '');
|
1109 | return str;
|
1110 | }
|
1111 |
|
1112 | function isAlpha(str) {
|
1113 | return /^[a-z0-9_\-]+$/i.test(str);
|
1114 | }
|
1115 |
|
1116 | function isKey(key) {
|
1117 | return /^[a-zA-Z0-9]+$/.test(key);
|
1118 | }
|
1119 |
|
1120 | function isLowerKey(key) {
|
1121 | if (!isKey(key))
|
1122 | return false;
|
1123 |
|
1124 | return !/[A-Z]/.test(key);
|
1125 | }
|
1126 |
|
1127 | function isUpperKey(key) {
|
1128 | if (!isKey(key))
|
1129 | return false;
|
1130 |
|
1131 | return !/[a-z]/.test(key);
|
1132 | }
|
1133 |
|
1134 | function _filter(name, a, b) {
|
1135 | for (const key of Object.keys(a)) {
|
1136 | if (key.length > name.length && key.indexOf(name) === 0) {
|
1137 | const sub = key.substring(name.length);
|
1138 | b[sub] = a[key];
|
1139 | }
|
1140 | }
|
1141 | }
|
1142 |
|
1143 | function fromFloat(num, exp) {
|
1144 | assert(typeof num === 'number' && isFinite(num));
|
1145 | assert(Number.isSafeInteger(exp));
|
1146 |
|
1147 | let str = num.toFixed(exp);
|
1148 | let sign = 1;
|
1149 |
|
1150 | if (str.length > 0 && str[0] === '-') {
|
1151 | str = str.substring(1);
|
1152 | sign = -1;
|
1153 | }
|
1154 |
|
1155 | let hi = str;
|
1156 | let lo = '0';
|
1157 |
|
1158 | const index = str.indexOf('.');
|
1159 |
|
1160 | if (index !== -1) {
|
1161 | hi = str.substring(0, index);
|
1162 | lo = str.substring(index + 1);
|
1163 | }
|
1164 |
|
1165 | hi = hi.replace(/^0+/, '');
|
1166 | lo = lo.replace(/0+$/, '');
|
1167 |
|
1168 | assert(hi.length <= 16 - exp,
|
1169 | 'Fixed number string exceeds 2^53-1.');
|
1170 |
|
1171 | assert(lo.length <= exp,
|
1172 | 'Too many decimal places in fixed number string.');
|
1173 |
|
1174 | if (hi.length === 0)
|
1175 | hi = '0';
|
1176 |
|
1177 | while (lo.length < exp)
|
1178 | lo += '0';
|
1179 |
|
1180 | if (lo.length === 0)
|
1181 | lo = '0';
|
1182 |
|
1183 | assert(/^\d+$/.test(hi) && /^\d+$/.test(lo),
|
1184 | 'Non-numeric characters in fixed number string.');
|
1185 |
|
1186 | hi = parseInt(hi, 10);
|
1187 | lo = parseInt(lo, 10);
|
1188 |
|
1189 | const mult = Math.pow(10, exp);
|
1190 | const maxLo = Number.MAX_SAFE_INTEGER % mult;
|
1191 | const maxHi = (Number.MAX_SAFE_INTEGER - maxLo) / mult;
|
1192 |
|
1193 | assert(hi < maxHi || (hi === maxHi && lo <= maxLo),
|
1194 | 'Fixed number string exceeds 2^53-1.');
|
1195 |
|
1196 | return sign * (hi * mult + lo);
|
1197 | }
|
1198 |
|
1199 |
|
1200 |
|
1201 |
|
1202 |
|
1203 | module.exports = Config;
|