util.js 2.83 KB
Newer Older
Natthaphong Malaphan's avatar
Natthaphong Malaphan committed
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
'use strict';
const fs = require('fs');
const path = require('path');
const types = require('./types');

// https://github.com/npm/npm/blob/latest/lib/config/core.js#L409-L423
const envReplace = str => {
	if (typeof str !== 'string' || !str) {
		return str;
	}

	// Replace any ${ENV} values with the appropriate environment
	const regex = /(\\*)\$\{([^}]+)\}/g;

	return str.replace(regex, (orig, esc, name) => {
		esc = esc.length > 0 && esc.length % 2;

		if (esc) {
			return orig;
		}

		if (process.env[name] === undefined) {
			throw new Error(`Failed to replace env in config: ${orig}`);
		}

		return process.env[name];
	});
};

// https://github.com/npm/npm/blob/latest/lib/config/core.js#L362-L407
const parseField = (field, key) => {
	if (typeof field !== 'string') {
		return field;
	}

	const typeList = [].concat(types[key]);
	const isPath = typeList.indexOf(path) !== -1;
	const isBool = typeList.indexOf(Boolean) !== -1;
	const isString = typeList.indexOf(String) !== -1;
	const isNumber = typeList.indexOf(Number) !== -1;

	field = `${field}`.trim();

	if (/^".*"$/.test(field)) {
		try {
			field = JSON.parse(field);
		} catch (err) {
			throw new Error(`Failed parsing JSON config key ${key}: ${field}`);
		}
	}

	if (isBool && !isString && field === '') {
		return true;
	}

	switch (field) { // eslint-disable-line default-case
		case 'true': {
			return true;
		}

		case 'false': {
			return false;
		}

		case 'null': {
			return null;
		}

		case 'undefined': {
			return undefined;
		}
	}

	field = envReplace(field);

	if (isPath) {
		const regex = process.platform === 'win32' ? /^~(\/|\\)/ : /^~\//;

		if (regex.test(field) && process.env.HOME) {
			field = path.resolve(process.env.HOME, field.substr(2));
		}

		field = path.resolve(field);
	}

	if (isNumber && !field.isNan()) {
		field = Number(field);
	}

	return field;
};

// https://github.com/npm/npm/blob/latest/lib/config/find-prefix.js
const findPrefix = name => {
	name = path.resolve(name);

	let walkedUp = false;

	while (path.basename(name) === 'node_modules') {
		name = path.dirname(name);
		walkedUp = true;
	}

	if (walkedUp) {
		return name;
	}

	const find = (name, original) => {
		const regex = /^[a-zA-Z]:(\\|\/)?$/;

		if (name === '/' || (process.platform === 'win32' && regex.test(name))) {
			return original;
		}

		try {
			const files = fs.readdirSync(name);

			if (files.indexOf('node_modules') !== -1 || files.indexOf('package.json') !== -1) {
				return name;
			}

			const dirname = path.dirname(name);

			if (dirname === name) {
				return original;
			}

			return find(dirname, original);
		} catch (err) {
			if (name === original) {
				if (err.code === 'ENOENT') {
					return original;
				}

				throw err;
			}

			return original;
		}
	};

	return find(name, name);
};

exports.envReplace = envReplace;
exports.findPrefix = findPrefix;
exports.parseField = parseField;