[6a3a178] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.lilconfigSync = exports.lilconfig = exports.defaultLoaders = void 0;
|
---|
| 4 | const path = require("path");
|
---|
| 5 | const fs = require("fs");
|
---|
| 6 | const os = require("os");
|
---|
| 7 | const fsReadFileAsync = fs.promises.readFile;
|
---|
| 8 | function getDefaultSearchPlaces(name) {
|
---|
| 9 | return [
|
---|
| 10 | 'package.json',
|
---|
| 11 | `.${name}rc.json`,
|
---|
| 12 | `.${name}rc.js`,
|
---|
| 13 | `${name}.config.js`,
|
---|
| 14 | `.${name}rc.cjs`,
|
---|
| 15 | `${name}.config.cjs`,
|
---|
| 16 | ];
|
---|
| 17 | }
|
---|
| 18 | function getSearchPaths(startDir, stopDir) {
|
---|
| 19 | return startDir
|
---|
| 20 | .split(path.sep)
|
---|
| 21 | .reduceRight((acc, _, ind, arr) => {
|
---|
| 22 | const currentPath = arr.slice(0, ind + 1).join(path.sep);
|
---|
| 23 | if (!acc.passedStopDir)
|
---|
| 24 | acc.searchPlaces.push(currentPath || path.sep);
|
---|
| 25 | if (currentPath === stopDir)
|
---|
| 26 | acc.passedStopDir = true;
|
---|
| 27 | return acc;
|
---|
| 28 | }, { searchPlaces: [], passedStopDir: false }).searchPlaces;
|
---|
| 29 | }
|
---|
| 30 | exports.defaultLoaders = Object.freeze({
|
---|
| 31 | '.js': require,
|
---|
| 32 | '.json': require,
|
---|
| 33 | '.cjs': require,
|
---|
| 34 | noExt(_, content) {
|
---|
| 35 | return JSON.parse(content);
|
---|
| 36 | },
|
---|
| 37 | });
|
---|
| 38 | function getExtDesc(ext) {
|
---|
| 39 | return ext === 'noExt' ? 'files without extensions' : `extension "${ext}"`;
|
---|
| 40 | }
|
---|
| 41 | function getOptions(name, options = {}) {
|
---|
| 42 | const conf = {
|
---|
| 43 | stopDir: os.homedir(),
|
---|
| 44 | searchPlaces: getDefaultSearchPlaces(name),
|
---|
| 45 | ignoreEmptySearchPlaces: true,
|
---|
| 46 | transform: (x) => x,
|
---|
| 47 | packageProp: [name],
|
---|
| 48 | ...options,
|
---|
| 49 | loaders: { ...exports.defaultLoaders, ...options.loaders },
|
---|
| 50 | };
|
---|
| 51 | conf.searchPlaces.forEach(place => {
|
---|
| 52 | const key = path.extname(place) || 'noExt';
|
---|
| 53 | const loader = conf.loaders[key];
|
---|
| 54 | if (!loader) {
|
---|
| 55 | throw new Error(`No loader specified for ${getExtDesc(key)}, so searchPlaces item "${place}" is invalid`);
|
---|
| 56 | }
|
---|
| 57 | if (typeof loader !== 'function') {
|
---|
| 58 | throw new Error(`loader for ${getExtDesc(key)} is not a function (type provided: "${typeof loader}"), so searchPlaces item "${place}" is invalid`);
|
---|
| 59 | }
|
---|
| 60 | });
|
---|
| 61 | return conf;
|
---|
| 62 | }
|
---|
| 63 | function getPackageProp(props, obj) {
|
---|
| 64 | if (typeof props === 'string' && props in obj)
|
---|
| 65 | return obj[props];
|
---|
| 66 | return ((Array.isArray(props) ? props : props.split('.')).reduce((acc, prop) => (acc === undefined ? acc : acc[prop]), obj) || null);
|
---|
| 67 | }
|
---|
| 68 | function getSearchItems(searchPlaces, searchPaths) {
|
---|
| 69 | return searchPaths.reduce((acc, searchPath) => {
|
---|
| 70 | searchPlaces.forEach(fileName => acc.push({
|
---|
| 71 | fileName,
|
---|
| 72 | filepath: path.join(searchPath, fileName),
|
---|
| 73 | loaderKey: path.extname(fileName) || 'noExt',
|
---|
| 74 | }));
|
---|
| 75 | return acc;
|
---|
| 76 | }, []);
|
---|
| 77 | }
|
---|
| 78 | function validateFilePath(filepath) {
|
---|
| 79 | if (!filepath)
|
---|
| 80 | throw new Error('load must pass a non-empty string');
|
---|
| 81 | }
|
---|
| 82 | function validateLoader(loader, ext) {
|
---|
| 83 | if (!loader)
|
---|
| 84 | throw new Error(`No loader specified for extension "${ext}"`);
|
---|
| 85 | if (typeof loader !== 'function')
|
---|
| 86 | throw new Error('loader is not a function');
|
---|
| 87 | }
|
---|
| 88 | function lilconfig(name, options) {
|
---|
| 89 | const { ignoreEmptySearchPlaces, loaders, packageProp, searchPlaces, stopDir, transform, } = getOptions(name, options);
|
---|
| 90 | return {
|
---|
| 91 | async search(searchFrom = process.cwd()) {
|
---|
| 92 | const searchPaths = getSearchPaths(searchFrom, stopDir);
|
---|
| 93 | const result = {
|
---|
| 94 | config: null,
|
---|
| 95 | filepath: '',
|
---|
| 96 | };
|
---|
| 97 | const searchItems = getSearchItems(searchPlaces, searchPaths);
|
---|
| 98 | for (const { fileName, filepath, loaderKey } of searchItems) {
|
---|
| 99 | try {
|
---|
| 100 | await fs.promises.access(filepath);
|
---|
| 101 | }
|
---|
| 102 | catch (_a) {
|
---|
| 103 | continue;
|
---|
| 104 | }
|
---|
| 105 | const content = String(await fsReadFileAsync(filepath));
|
---|
| 106 | const loader = loaders[loaderKey];
|
---|
| 107 | if (fileName === 'package.json') {
|
---|
| 108 | const pkg = loader(filepath, content);
|
---|
| 109 | const maybeConfig = getPackageProp(packageProp, pkg);
|
---|
| 110 | if (maybeConfig != null) {
|
---|
| 111 | result.config = maybeConfig;
|
---|
| 112 | result.filepath = filepath;
|
---|
| 113 | break;
|
---|
| 114 | }
|
---|
| 115 | continue;
|
---|
| 116 | }
|
---|
| 117 | const isEmpty = content.trim() === '';
|
---|
| 118 | if (isEmpty && ignoreEmptySearchPlaces)
|
---|
| 119 | continue;
|
---|
| 120 | if (isEmpty) {
|
---|
| 121 | result.isEmpty = true;
|
---|
| 122 | result.config = undefined;
|
---|
| 123 | }
|
---|
| 124 | else {
|
---|
| 125 | validateLoader(loader, loaderKey);
|
---|
| 126 | result.config = loader(filepath, content);
|
---|
| 127 | }
|
---|
| 128 | result.filepath = filepath;
|
---|
| 129 | break;
|
---|
| 130 | }
|
---|
| 131 | if (result.filepath === '' && result.config === null)
|
---|
| 132 | return transform(null);
|
---|
| 133 | return transform(result);
|
---|
| 134 | },
|
---|
| 135 | async load(filepath) {
|
---|
| 136 | validateFilePath(filepath);
|
---|
| 137 | const { base, ext } = path.parse(filepath);
|
---|
| 138 | const loaderKey = ext || 'noExt';
|
---|
| 139 | const loader = loaders[loaderKey];
|
---|
| 140 | validateLoader(loader, loaderKey);
|
---|
| 141 | const content = String(await fsReadFileAsync(filepath));
|
---|
| 142 | if (base === 'package.json') {
|
---|
| 143 | const pkg = await loader(filepath, content);
|
---|
| 144 | return transform({
|
---|
| 145 | config: getPackageProp(packageProp, pkg),
|
---|
| 146 | filepath,
|
---|
| 147 | });
|
---|
| 148 | }
|
---|
| 149 | const result = {
|
---|
| 150 | config: null,
|
---|
| 151 | filepath,
|
---|
| 152 | };
|
---|
| 153 | const isEmpty = content.trim() === '';
|
---|
| 154 | if (isEmpty && ignoreEmptySearchPlaces)
|
---|
| 155 | return transform({
|
---|
| 156 | config: undefined,
|
---|
| 157 | filepath,
|
---|
| 158 | isEmpty: true,
|
---|
| 159 | });
|
---|
| 160 | result.config = isEmpty
|
---|
| 161 | ? undefined
|
---|
| 162 | : await loader(filepath, content);
|
---|
| 163 | return transform(isEmpty ? { ...result, isEmpty, config: undefined } : result);
|
---|
| 164 | },
|
---|
| 165 | };
|
---|
| 166 | }
|
---|
| 167 | exports.lilconfig = lilconfig;
|
---|
| 168 | function lilconfigSync(name, options) {
|
---|
| 169 | const { ignoreEmptySearchPlaces, loaders, packageProp, searchPlaces, stopDir, transform, } = getOptions(name, options);
|
---|
| 170 | return {
|
---|
| 171 | search(searchFrom = process.cwd()) {
|
---|
| 172 | const searchPaths = getSearchPaths(searchFrom, stopDir);
|
---|
| 173 | const result = {
|
---|
| 174 | config: null,
|
---|
| 175 | filepath: '',
|
---|
| 176 | };
|
---|
| 177 | const searchItems = getSearchItems(searchPlaces, searchPaths);
|
---|
| 178 | for (const { fileName, filepath, loaderKey } of searchItems) {
|
---|
| 179 | try {
|
---|
| 180 | fs.accessSync(filepath);
|
---|
| 181 | }
|
---|
| 182 | catch (_a) {
|
---|
| 183 | continue;
|
---|
| 184 | }
|
---|
| 185 | const loader = loaders[loaderKey];
|
---|
| 186 | const content = String(fs.readFileSync(filepath));
|
---|
| 187 | if (fileName === 'package.json') {
|
---|
| 188 | const pkg = loader(filepath, content);
|
---|
| 189 | const maybeConfig = getPackageProp(packageProp, pkg);
|
---|
| 190 | if (maybeConfig != null) {
|
---|
| 191 | result.config = maybeConfig;
|
---|
| 192 | result.filepath = filepath;
|
---|
| 193 | break;
|
---|
| 194 | }
|
---|
| 195 | continue;
|
---|
| 196 | }
|
---|
| 197 | const isEmpty = content.trim() === '';
|
---|
| 198 | if (isEmpty && ignoreEmptySearchPlaces)
|
---|
| 199 | continue;
|
---|
| 200 | if (isEmpty) {
|
---|
| 201 | result.isEmpty = true;
|
---|
| 202 | result.config = undefined;
|
---|
| 203 | }
|
---|
| 204 | else {
|
---|
| 205 | validateLoader(loader, loaderKey);
|
---|
| 206 | result.config = loader(filepath, content);
|
---|
| 207 | }
|
---|
| 208 | result.filepath = filepath;
|
---|
| 209 | break;
|
---|
| 210 | }
|
---|
| 211 | if (result.filepath === '' && result.config === null)
|
---|
| 212 | return transform(null);
|
---|
| 213 | return transform(result);
|
---|
| 214 | },
|
---|
| 215 | load(filepath) {
|
---|
| 216 | validateFilePath(filepath);
|
---|
| 217 | const { base, ext } = path.parse(filepath);
|
---|
| 218 | const loaderKey = ext || 'noExt';
|
---|
| 219 | const loader = loaders[loaderKey];
|
---|
| 220 | validateLoader(loader, loaderKey);
|
---|
| 221 | const content = String(fs.readFileSync(filepath));
|
---|
| 222 | if (base === 'package.json') {
|
---|
| 223 | const pkg = loader(filepath, content);
|
---|
| 224 | return transform({
|
---|
| 225 | config: getPackageProp(packageProp, pkg),
|
---|
| 226 | filepath,
|
---|
| 227 | });
|
---|
| 228 | }
|
---|
| 229 | const result = {
|
---|
| 230 | config: null,
|
---|
| 231 | filepath,
|
---|
| 232 | };
|
---|
| 233 | const isEmpty = content.trim() === '';
|
---|
| 234 | if (isEmpty && ignoreEmptySearchPlaces)
|
---|
| 235 | return transform({
|
---|
| 236 | filepath,
|
---|
| 237 | config: undefined,
|
---|
| 238 | isEmpty: true,
|
---|
| 239 | });
|
---|
| 240 | result.config = isEmpty ? undefined : loader(filepath, content);
|
---|
| 241 | return transform(isEmpty ? { ...result, isEmpty, config: undefined } : result);
|
---|
| 242 | },
|
---|
| 243 | };
|
---|
| 244 | }
|
---|
| 245 | exports.lilconfigSync = lilconfigSync;
|
---|