All files / src/config config.ts

96.72% Statements 59/61
86.36% Branches 19/22
100% Functions 14/14
96.67% Lines 58/60
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 1491x     1x 1x 1x   1x   1x             2206x 5x   2201x       1x       1x 1x 1x     572x       55x       14x 10x     4x     4x 8x 1x 1x       4x       38x             1253x       1253x 1253x     1253x 1253x 13x 13x                     550x         604x 34x     570x 570x 570x     570x     570x 570x 570x 570x             570x       5x 3x   2x       1816x 1816x       3x       515x 515x 2206x 2206x 1774x 432x     2206x 2206x   515x      
import { MetaTable } from '../meta';
import { ConfigData } from './config-data';
 
const fs = require('fs');
const path = require('path');
const deepmerge = require('deepmerge');
 
const recommended = require('./recommended');
 
const parseSeverityLut = [
	'disable',
	'warn',
	'error',
];
 
function parseSeverity(value: string | number){
	if (typeof value === 'number'){
		return value;
	} else {
		return parseSeverityLut.indexOf(value.toLowerCase());
	}
}
 
export class Config {
	private config: ConfigData;
	protected metaTable: MetaTable;
 
	public static readonly SEVERITY_DISABLED = 0;
	public static readonly SEVERITY_WARN = 1;
	public static readonly SEVERITY_ERROR = 2;
 
	static empty(): Config {
		return new Config();
	}
 
	static fromObject(options: Object): Config {
		return new Config(options);
	}
 
	static fromFile(filename: string): Config {
		if (filename === 'htmlvalidate:recommended'){
			return Config.fromObject(recommended);
		}
 
		const json = require(filename);
 
		/* expand any relative paths */
		for (const key of ['extends', 'elements']){
			if (!json[key]) continue;
			json[key] = json[key].map((ref: string) => {
				return Config.expandRelative(ref, path.dirname(filename));
			});
		}
 
		return new Config(json);
	}
 
	static defaultConfig(): Config {
		return new Config({
			extends: [],
			rules: {},
		});
	}
 
	constructor(options?: any){
		this.config = {
			extends: [],
			rules: {},
		};
		this.mergeInternal(options || {});
		this.metaTable = null;
 
		/* process and extended configs */
		const self = this;
		this.config.extends.forEach(function(ref: string){
			const base = Config.fromFile(ref);
			self.config = base.mergeInternal(self.config);
		});
	}
 
	/**
	 * Returns a new configuration as a merge of the two. Entries from the passed
	 * object takes priority over this object.
	 *
	 * @param {Config} rhs - Configuration to merge with this one.
	 */
	public merge(rhs: Config){
		return new Config(this.mergeInternal(rhs.config));
	}
 
	getMetaTable(){
		/* use cached table if it exists */
		if (this.metaTable){
			return this.metaTable;
		}
 
		this.metaTable = new MetaTable();
		const source = this.config.elements || ['html5'];
		const root = path.resolve(__dirname, '..', '..');
 
		/* load from all entries */
		for (const entry of source){
 
			/* try searching builtin metadata */
			const filename = `${root}/elements/${entry}.json`;
			Eif (fs.existsSync(filename)){
				this.metaTable.loadFromFile(filename);
				continue;
			}
 
			/* assume it is loadable with require() */
			this.metaTable.loadFromObject(require(entry));
		}
 
		return this.metaTable;
	}
 
	static expandRelative(src: string, currentPath: string): string {
		if (src[0] === '.'){
			return path.normalize(`${currentPath}/${src}`);
		}
		return src;
	}
 
	private mergeInternal(config: ConfigData): ConfigData {
		this.config = deepmerge(this.config, config);
		return this.config;
	}
 
	get(): ConfigData {
		return Object.assign({}, this.config);
	}
 
	getRules(){
		const rules = Object.assign({}, this.config.rules || {});
		for (const name in rules){
			let options = rules[name];
			if (!Array.isArray(options)){
				options = [options, {}];
			} else Iif (options.length === 1){
				options.push({});
			}
			options[0] = parseSeverity(options[0]);
			rules[name] = options;
		}
		return rules;
	}
}