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