You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
9.3 KiB
247 lines
9.3 KiB
"use strict"; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.lilconfigSync = exports.lilconfig = exports.defaultLoaders = void 0; |
|
const path = require("path"); |
|
const fs = require("fs"); |
|
const os = require("os"); |
|
const fsReadFileAsync = fs.promises.readFile; |
|
function getDefaultSearchPlaces(name) { |
|
return [ |
|
'package.json', |
|
`.${name}rc.json`, |
|
`.${name}rc.js`, |
|
`${name}.config.js`, |
|
`.${name}rc.cjs`, |
|
`${name}.config.cjs`, |
|
]; |
|
} |
|
function getSearchPaths(startDir, stopDir) { |
|
return startDir |
|
.split(path.sep) |
|
.reduceRight((acc, _, ind, arr) => { |
|
const currentPath = arr.slice(0, ind + 1).join(path.sep); |
|
if (!acc.passedStopDir) |
|
acc.searchPlaces.push(currentPath || path.sep); |
|
if (currentPath === stopDir) |
|
acc.passedStopDir = true; |
|
return acc; |
|
}, { searchPlaces: [], passedStopDir: false }).searchPlaces; |
|
} |
|
exports.defaultLoaders = Object.freeze({ |
|
'.js': require, |
|
'.json': require, |
|
'.cjs': require, |
|
noExt(_, content) { |
|
return JSON.parse(content); |
|
}, |
|
}); |
|
function getExtDesc(ext) { |
|
return ext === 'noExt' ? 'files without extensions' : `extension "${ext}"`; |
|
} |
|
function getOptions(name, options = {}) { |
|
const conf = { |
|
stopDir: os.homedir(), |
|
searchPlaces: getDefaultSearchPlaces(name), |
|
ignoreEmptySearchPlaces: true, |
|
transform: (x) => x, |
|
packageProp: [name], |
|
...options, |
|
loaders: { ...exports.defaultLoaders, ...options.loaders }, |
|
}; |
|
conf.searchPlaces.forEach(place => { |
|
const key = path.extname(place) || 'noExt'; |
|
const loader = conf.loaders[key]; |
|
if (!loader) { |
|
throw new Error(`No loader specified for ${getExtDesc(key)}, so searchPlaces item "${place}" is invalid`); |
|
} |
|
if (typeof loader !== 'function') { |
|
throw new Error(`loader for ${getExtDesc(key)} is not a function (type provided: "${typeof loader}"), so searchPlaces item "${place}" is invalid`); |
|
} |
|
}); |
|
return conf; |
|
} |
|
function getPackageProp(props, obj) { |
|
if (typeof props === 'string' && props in obj) |
|
return obj[props]; |
|
return ((Array.isArray(props) ? props : props.split('.')).reduce((acc, prop) => (acc === undefined ? acc : acc[prop]), obj) || null); |
|
} |
|
function getSearchItems(searchPlaces, searchPaths) { |
|
return searchPaths.reduce((acc, searchPath) => { |
|
searchPlaces.forEach(fileName => acc.push({ |
|
fileName, |
|
filepath: path.join(searchPath, fileName), |
|
loaderKey: path.extname(fileName) || 'noExt', |
|
})); |
|
return acc; |
|
}, []); |
|
} |
|
function validateFilePath(filepath) { |
|
if (!filepath) |
|
throw new Error('load must pass a non-empty string'); |
|
} |
|
function validateLoader(loader, ext) { |
|
if (!loader) |
|
throw new Error(`No loader specified for extension "${ext}"`); |
|
if (typeof loader !== 'function') |
|
throw new Error('loader is not a function'); |
|
} |
|
function lilconfig(name, options) { |
|
const { ignoreEmptySearchPlaces, loaders, packageProp, searchPlaces, stopDir, transform, } = getOptions(name, options); |
|
return { |
|
async search(searchFrom = process.cwd()) { |
|
const searchPaths = getSearchPaths(searchFrom, stopDir); |
|
const result = { |
|
config: null, |
|
filepath: '', |
|
}; |
|
const searchItems = getSearchItems(searchPlaces, searchPaths); |
|
for (const { fileName, filepath, loaderKey } of searchItems) { |
|
try { |
|
await fs.promises.access(filepath); |
|
} |
|
catch (_a) { |
|
continue; |
|
} |
|
const content = String(await fsReadFileAsync(filepath)); |
|
const loader = loaders[loaderKey]; |
|
if (fileName === 'package.json') { |
|
const pkg = await loader(filepath, content); |
|
const maybeConfig = getPackageProp(packageProp, pkg); |
|
if (maybeConfig != null) { |
|
result.config = maybeConfig; |
|
result.filepath = filepath; |
|
break; |
|
} |
|
continue; |
|
} |
|
const isEmpty = content.trim() === ''; |
|
if (isEmpty && ignoreEmptySearchPlaces) |
|
continue; |
|
if (isEmpty) { |
|
result.isEmpty = true; |
|
result.config = undefined; |
|
} |
|
else { |
|
validateLoader(loader, loaderKey); |
|
result.config = await loader(filepath, content); |
|
} |
|
result.filepath = filepath; |
|
break; |
|
} |
|
if (result.filepath === '' && result.config === null) |
|
return transform(null); |
|
return transform(result); |
|
}, |
|
async load(filepath) { |
|
validateFilePath(filepath); |
|
const absPath = path.resolve(process.cwd(), filepath); |
|
const { base, ext } = path.parse(absPath); |
|
const loaderKey = ext || 'noExt'; |
|
const loader = loaders[loaderKey]; |
|
validateLoader(loader, loaderKey); |
|
const content = String(await fsReadFileAsync(absPath)); |
|
if (base === 'package.json') { |
|
const pkg = await loader(absPath, content); |
|
return transform({ |
|
config: getPackageProp(packageProp, pkg), |
|
filepath: absPath, |
|
}); |
|
} |
|
const result = { |
|
config: null, |
|
filepath: absPath, |
|
}; |
|
const isEmpty = content.trim() === ''; |
|
if (isEmpty && ignoreEmptySearchPlaces) |
|
return transform({ |
|
config: undefined, |
|
filepath: absPath, |
|
isEmpty: true, |
|
}); |
|
result.config = isEmpty |
|
? undefined |
|
: await loader(absPath, content); |
|
return transform(isEmpty ? { ...result, isEmpty, config: undefined } : result); |
|
}, |
|
}; |
|
} |
|
exports.lilconfig = lilconfig; |
|
function lilconfigSync(name, options) { |
|
const { ignoreEmptySearchPlaces, loaders, packageProp, searchPlaces, stopDir, transform, } = getOptions(name, options); |
|
return { |
|
search(searchFrom = process.cwd()) { |
|
const searchPaths = getSearchPaths(searchFrom, stopDir); |
|
const result = { |
|
config: null, |
|
filepath: '', |
|
}; |
|
const searchItems = getSearchItems(searchPlaces, searchPaths); |
|
for (const { fileName, filepath, loaderKey } of searchItems) { |
|
try { |
|
fs.accessSync(filepath); |
|
} |
|
catch (_a) { |
|
continue; |
|
} |
|
const loader = loaders[loaderKey]; |
|
const content = String(fs.readFileSync(filepath)); |
|
if (fileName === 'package.json') { |
|
const pkg = loader(filepath, content); |
|
const maybeConfig = getPackageProp(packageProp, pkg); |
|
if (maybeConfig != null) { |
|
result.config = maybeConfig; |
|
result.filepath = filepath; |
|
break; |
|
} |
|
continue; |
|
} |
|
const isEmpty = content.trim() === ''; |
|
if (isEmpty && ignoreEmptySearchPlaces) |
|
continue; |
|
if (isEmpty) { |
|
result.isEmpty = true; |
|
result.config = undefined; |
|
} |
|
else { |
|
validateLoader(loader, loaderKey); |
|
result.config = loader(filepath, content); |
|
} |
|
result.filepath = filepath; |
|
break; |
|
} |
|
if (result.filepath === '' && result.config === null) |
|
return transform(null); |
|
return transform(result); |
|
}, |
|
load(filepath) { |
|
validateFilePath(filepath); |
|
const absPath = path.resolve(process.cwd(), filepath); |
|
const { base, ext } = path.parse(absPath); |
|
const loaderKey = ext || 'noExt'; |
|
const loader = loaders[loaderKey]; |
|
validateLoader(loader, loaderKey); |
|
const content = String(fs.readFileSync(absPath)); |
|
if (base === 'package.json') { |
|
const pkg = loader(absPath, content); |
|
return transform({ |
|
config: getPackageProp(packageProp, pkg), |
|
filepath: absPath, |
|
}); |
|
} |
|
const result = { |
|
config: null, |
|
filepath: absPath, |
|
}; |
|
const isEmpty = content.trim() === ''; |
|
if (isEmpty && ignoreEmptySearchPlaces) |
|
return transform({ |
|
filepath: absPath, |
|
config: undefined, |
|
isEmpty: true, |
|
}); |
|
result.config = isEmpty ? undefined : loader(absPath, content); |
|
return transform(isEmpty ? { ...result, isEmpty, config: undefined } : result); |
|
}, |
|
}; |
|
} |
|
exports.lilconfigSync = lilconfigSync;
|
|
|