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;
|
---|