Add <%br= %> jqhtml syntax docs, class override detection, npm update
Document event handler placement and model fetch clarification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
38
node_modules/lilconfig/dist/index.d.ts
generated
vendored
38
node_modules/lilconfig/dist/index.d.ts
generated
vendored
@@ -1,38 +0,0 @@
|
||||
export declare type LilconfigResult = null | {
|
||||
filepath: string;
|
||||
config: any;
|
||||
isEmpty?: boolean;
|
||||
};
|
||||
interface OptionsBase {
|
||||
stopDir?: string;
|
||||
searchPlaces?: string[];
|
||||
ignoreEmptySearchPlaces?: boolean;
|
||||
packageProp?: string | string[];
|
||||
}
|
||||
export declare type Transform = TransformSync | ((result: LilconfigResult) => Promise<LilconfigResult>);
|
||||
export declare type TransformSync = (result: LilconfigResult) => LilconfigResult;
|
||||
declare type LoaderResult = any;
|
||||
export declare type LoaderSync = (filepath: string, content: string) => LoaderResult;
|
||||
export declare type Loader = LoaderSync | ((filepath: string, content: string) => Promise<LoaderResult>);
|
||||
export declare type Loaders = Record<string, Loader>;
|
||||
export declare type LoadersSync = Record<string, LoaderSync>;
|
||||
export interface Options extends OptionsBase {
|
||||
loaders?: Loaders;
|
||||
transform?: Transform;
|
||||
}
|
||||
export interface OptionsSync extends OptionsBase {
|
||||
loaders?: LoadersSync;
|
||||
transform?: TransformSync;
|
||||
}
|
||||
export declare const defaultLoaders: LoadersSync;
|
||||
declare type AsyncSearcher = {
|
||||
search(searchFrom?: string): Promise<LilconfigResult>;
|
||||
load(filepath: string): Promise<LilconfigResult>;
|
||||
};
|
||||
export declare function lilconfig(name: string, options?: Partial<Options>): AsyncSearcher;
|
||||
declare type SyncSearcher = {
|
||||
search(searchFrom?: string): LilconfigResult;
|
||||
load(filepath: string): LilconfigResult;
|
||||
};
|
||||
export declare function lilconfigSync(name: string, options?: OptionsSync): SyncSearcher;
|
||||
export {};
|
||||
251
node_modules/lilconfig/dist/index.js
generated
vendored
251
node_modules/lilconfig/dist/index.js
generated
vendored
@@ -1,251 +0,0 @@
|
||||
"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}rc.cjs`,
|
||||
`.config/${name}rc`,
|
||||
`.config/${name}rc.json`,
|
||||
`.config/${name}rc.js`,
|
||||
`.config/${name}rc.cjs`,
|
||||
`${name}.config.js`,
|
||||
`${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(sp => acc.push({
|
||||
searchPlace: sp,
|
||||
filepath: path.join(searchPath, sp),
|
||||
loaderKey: path.extname(sp) || '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 { searchPlace, filepath, loaderKey } of searchItems) {
|
||||
try {
|
||||
await fs.promises.access(filepath);
|
||||
}
|
||||
catch (_a) {
|
||||
continue;
|
||||
}
|
||||
const content = String(await fsReadFileAsync(filepath));
|
||||
const loader = loaders[loaderKey];
|
||||
if (searchPlace === '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 { searchPlace, filepath, loaderKey } of searchItems) {
|
||||
try {
|
||||
fs.accessSync(filepath);
|
||||
}
|
||||
catch (_a) {
|
||||
continue;
|
||||
}
|
||||
const loader = loaders[loaderKey];
|
||||
const content = String(fs.readFileSync(filepath));
|
||||
if (searchPlace === '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;
|
||||
86
node_modules/lilconfig/package.json
generated
vendored
86
node_modules/lilconfig/package.json
generated
vendored
@@ -1,48 +1,42 @@
|
||||
{
|
||||
"name": "lilconfig",
|
||||
"version": "2.1.0",
|
||||
"description": "A zero-dependency alternative to cosmiconfig",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"prebuild": "npm run clean",
|
||||
"build": "tsc --declaration",
|
||||
"postbuild": "du -h ./dist/*",
|
||||
"clean": "rm -rf ./dist",
|
||||
"test": "jest --coverage",
|
||||
"lint": "eslint ./src/*.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"cosmiconfig",
|
||||
"config",
|
||||
"configuration",
|
||||
"search"
|
||||
],
|
||||
"files": [
|
||||
"dist/*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/antonk52/lilconfig"
|
||||
},
|
||||
"bugs": "https://github.com/antonk52/lilconfig/issues",
|
||||
"author": "antonk52",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "^14.18.36",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"cosmiconfig": "^7.1.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"jest": "^27.3.1",
|
||||
"prettier": "^2.8.4",
|
||||
"ts-jest": "27.0.7",
|
||||
"typescript": "4.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
"name": "lilconfig",
|
||||
"version": "3.1.3",
|
||||
"description": "A zero-dependency alternative to cosmiconfig",
|
||||
"main": "src/index.js",
|
||||
"types": "src/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules ./node_modules/.bin/jest --coverage",
|
||||
"lint": "biome ci ./src",
|
||||
"types": "tsc"
|
||||
},
|
||||
"keywords": [
|
||||
"cosmiconfig",
|
||||
"config",
|
||||
"configuration",
|
||||
"search"
|
||||
],
|
||||
"files": [
|
||||
"src/index.*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/antonk52/lilconfig"
|
||||
},
|
||||
"bugs": "https://github.com/antonk52/lilconfig/issues",
|
||||
"author": "antonk52",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.6.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^14.18.63",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
"cosmiconfig": "^8.3.6",
|
||||
"jest": "^29.7.0",
|
||||
"typescript": "^5.3.3",
|
||||
"uvu": "^0.5.6"
|
||||
},
|
||||
"funding": "https://github.com/sponsors/antonk52",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
|
||||
34
node_modules/lilconfig/readme.md
generated
vendored
34
node_modules/lilconfig/readme.md
generated
vendored
@@ -42,16 +42,18 @@ lilconfigSync(
|
||||
*/
|
||||
```
|
||||
|
||||
## ESM
|
||||
|
||||
ESM configs can be loaded with **async API only**. Specifically `js` files in projects with `"type": "module"` in `package.json` or `mjs` files.
|
||||
|
||||
## Difference to `cosmiconfig`
|
||||
Lilconfig does not intend to be 100% compatible with `cosmiconfig` but tries to mimic it where possible. The key differences are:
|
||||
- **no** support for yaml files out of the box(`lilconfig` attempts to parse files with no extension as JSON instead of YAML). You can still add the support for YAML files by providing a loader, see an [example](#yaml-loader) below.
|
||||
- **no** cache
|
||||
Lilconfig does not intend to be 100% compatible with `cosmiconfig` but tries to mimic it where possible. The key difference is **no** support for yaml files out of the box(`lilconfig` attempts to parse files with no extension as JSON instead of YAML). You can still add the support for YAML files by providing a loader, see an [example](#yaml-loader) below.
|
||||
|
||||
### Options difference between the two.
|
||||
|
||||
|cosmiconfig option | lilconfig |
|
||||
|------------------------|-----------|
|
||||
|cache | ❌ |
|
||||
|cache | ✅ |
|
||||
|loaders | ✅ |
|
||||
|ignoreEmptySearchPlaces | ✅ |
|
||||
|packageProp | ✅ |
|
||||
@@ -89,30 +91,8 @@ lilconfig('myapp', options)
|
||||
});
|
||||
```
|
||||
|
||||
### ESM loader
|
||||
|
||||
Lilconfig v2 does not support ESM modules out of the box. However, you can support it with a custom a loader. Note that this will only work with the async `lilconfig` function and won't work with the sync `lilconfigSync`.
|
||||
|
||||
```js
|
||||
import {lilconfig} from 'lilconfig';
|
||||
|
||||
const loadEsm = filepath => import(filepath);
|
||||
|
||||
lilconfig('myapp', {
|
||||
loaders: {
|
||||
'.js': loadEsm,
|
||||
'.mjs': loadEsm,
|
||||
}
|
||||
})
|
||||
.search()
|
||||
.then(result => {
|
||||
result // {config, filepath}
|
||||
|
||||
result.config.default // if config uses `export default`
|
||||
});
|
||||
```
|
||||
|
||||
## Version correlation
|
||||
|
||||
- lilconig v1 → cosmiconfig v6
|
||||
- lilconig v2 → cosmiconfig v7
|
||||
- lilconig v3 → cosmiconfig v8
|
||||
|
||||
54
node_modules/lilconfig/src/index.d.ts
generated
vendored
Normal file
54
node_modules/lilconfig/src/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
export type LilconfigResult = null | {
|
||||
filepath: string;
|
||||
config: any;
|
||||
isEmpty?: boolean;
|
||||
};
|
||||
interface OptionsBase {
|
||||
cache?: boolean;
|
||||
stopDir?: string;
|
||||
searchPlaces?: string[];
|
||||
ignoreEmptySearchPlaces?: boolean;
|
||||
packageProp?: string | string[];
|
||||
}
|
||||
export type Transform =
|
||||
| TransformSync
|
||||
| ((result: LilconfigResult) => Promise<LilconfigResult>);
|
||||
export type TransformSync = (result: LilconfigResult) => LilconfigResult;
|
||||
type LoaderResult = any;
|
||||
export type LoaderSync = (filepath: string, content: string) => LoaderResult;
|
||||
export type Loader =
|
||||
| LoaderSync
|
||||
| ((filepath: string, content: string) => Promise<LoaderResult>);
|
||||
export type Loaders = Record<string, Loader>;
|
||||
export type LoadersSync = Record<string, LoaderSync>;
|
||||
export interface Options extends OptionsBase {
|
||||
loaders?: Loaders;
|
||||
transform?: Transform;
|
||||
}
|
||||
export interface OptionsSync extends OptionsBase {
|
||||
loaders?: LoadersSync;
|
||||
transform?: TransformSync;
|
||||
}
|
||||
export declare const defaultLoadersSync: LoadersSync;
|
||||
export declare const defaultLoaders: Loaders;
|
||||
type ClearCaches = {
|
||||
clearLoadCache: () => void;
|
||||
clearSearchCache: () => void;
|
||||
clearCaches: () => void;
|
||||
};
|
||||
type AsyncSearcher = {
|
||||
search(searchFrom?: string): Promise<LilconfigResult>;
|
||||
load(filepath: string): Promise<LilconfigResult>;
|
||||
} & ClearCaches;
|
||||
export declare function lilconfig(
|
||||
name: string,
|
||||
options?: Partial<Options>,
|
||||
): AsyncSearcher;
|
||||
type SyncSearcher = {
|
||||
search(searchFrom?: string): LilconfigResult;
|
||||
load(filepath: string): LilconfigResult;
|
||||
} & ClearCaches;
|
||||
export declare function lilconfigSync(
|
||||
name: string,
|
||||
options?: OptionsSync,
|
||||
): SyncSearcher;
|
||||
460
node_modules/lilconfig/src/index.js
generated
vendored
Normal file
460
node_modules/lilconfig/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
// @ts-check
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const url = require('url');
|
||||
|
||||
const fsReadFileAsync = fs.promises.readFile;
|
||||
|
||||
/** @type {(name: string, sync: boolean) => string[]} */
|
||||
function getDefaultSearchPlaces(name, sync) {
|
||||
return [
|
||||
'package.json',
|
||||
`.${name}rc.json`,
|
||||
`.${name}rc.js`,
|
||||
`.${name}rc.cjs`,
|
||||
...(sync ? [] : [`.${name}rc.mjs`]),
|
||||
`.config/${name}rc`,
|
||||
`.config/${name}rc.json`,
|
||||
`.config/${name}rc.js`,
|
||||
`.config/${name}rc.cjs`,
|
||||
...(sync ? [] : [`.config/${name}rc.mjs`]),
|
||||
`${name}.config.js`,
|
||||
`${name}.config.cjs`,
|
||||
...(sync ? [] : [`${name}.config.mjs`]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(p: string) => string}
|
||||
*
|
||||
* see #17
|
||||
* On *nix, if cwd is not under homedir,
|
||||
* the last path will be '', ('/build' -> '')
|
||||
* but it should be '/' actually.
|
||||
* And on Windows, this will never happen. ('C:\build' -> 'C:')
|
||||
*/
|
||||
function parentDir(p) {
|
||||
return path.dirname(p) || path.sep;
|
||||
}
|
||||
|
||||
/** @type {import('./index').LoaderSync} */
|
||||
const jsonLoader = (_, content) => JSON.parse(content);
|
||||
// Use plain require in webpack context for dynamic import
|
||||
const requireFunc =
|
||||
typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
|
||||
/** @type {import('./index').LoadersSync} */
|
||||
const defaultLoadersSync = Object.freeze({
|
||||
'.js': requireFunc,
|
||||
'.json': requireFunc,
|
||||
'.cjs': requireFunc,
|
||||
noExt: jsonLoader,
|
||||
});
|
||||
module.exports.defaultLoadersSync = defaultLoadersSync;
|
||||
|
||||
/** @type {import('./index').Loader} */
|
||||
const dynamicImport = async id => {
|
||||
try {
|
||||
const fileUrl = url.pathToFileURL(id).href;
|
||||
const mod = await import(/* webpackIgnore: true */ fileUrl);
|
||||
|
||||
return mod.default;
|
||||
} catch (e) {
|
||||
try {
|
||||
return requireFunc(id);
|
||||
} catch (/** @type {any} */ requireE) {
|
||||
if (
|
||||
requireE.code === 'ERR_REQUIRE_ESM' ||
|
||||
(requireE instanceof SyntaxError &&
|
||||
requireE
|
||||
.toString()
|
||||
.includes('Cannot use import statement outside a module'))
|
||||
) {
|
||||
throw e;
|
||||
}
|
||||
throw requireE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {import('./index').Loaders} */
|
||||
const defaultLoaders = Object.freeze({
|
||||
'.js': dynamicImport,
|
||||
'.mjs': dynamicImport,
|
||||
'.cjs': dynamicImport,
|
||||
'.json': jsonLoader,
|
||||
noExt: jsonLoader,
|
||||
});
|
||||
module.exports.defaultLoaders = defaultLoaders;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {import('./index').Options | import('./index').OptionsSync} options
|
||||
* @param {boolean} sync
|
||||
* @returns {Required<import('./index').Options | import('./index').OptionsSync>}
|
||||
*/
|
||||
function getOptions(name, options, sync) {
|
||||
/** @type {Required<import('./index').Options>} */
|
||||
const conf = {
|
||||
stopDir: os.homedir(),
|
||||
searchPlaces: getDefaultSearchPlaces(name, sync),
|
||||
ignoreEmptySearchPlaces: true,
|
||||
cache: true,
|
||||
transform: x => x,
|
||||
packageProp: [name],
|
||||
...options,
|
||||
loaders: {
|
||||
...(sync ? defaultLoadersSync : defaultLoaders),
|
||||
...options.loaders,
|
||||
},
|
||||
};
|
||||
conf.searchPlaces.forEach(place => {
|
||||
const key = path.extname(place) || 'noExt';
|
||||
const loader = conf.loaders[key];
|
||||
if (!loader) {
|
||||
throw new Error(`Missing loader for extension "${place}"`);
|
||||
}
|
||||
|
||||
if (typeof loader !== 'function') {
|
||||
throw new Error(
|
||||
`Loader for extension "${place}" is not a function: Received ${typeof loader}.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
/** @type {(props: string | string[], obj: Record<string, any>) => unknown} */
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {string} filepath */
|
||||
function validateFilePath(filepath) {
|
||||
if (!filepath) throw new Error('load must pass a non-empty string');
|
||||
}
|
||||
|
||||
/** @type {(loader: import('./index').Loader, ext: string) => void} */
|
||||
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');
|
||||
}
|
||||
|
||||
/** @type {(enableCache: boolean) => <T>(c: Map<string, T>, filepath: string, res: T) => T} */
|
||||
const makeEmplace = enableCache => (c, filepath, res) => {
|
||||
if (enableCache) c.set(filepath, res);
|
||||
return res;
|
||||
};
|
||||
|
||||
/** @type {import('./index').lilconfig} */
|
||||
module.exports.lilconfig = function lilconfig(name, options) {
|
||||
const {
|
||||
ignoreEmptySearchPlaces,
|
||||
loaders,
|
||||
packageProp,
|
||||
searchPlaces,
|
||||
stopDir,
|
||||
transform,
|
||||
cache,
|
||||
} = getOptions(name, options ?? {}, false);
|
||||
const searchCache = new Map();
|
||||
const loadCache = new Map();
|
||||
const emplace = makeEmplace(cache);
|
||||
|
||||
return {
|
||||
async search(searchFrom = process.cwd()) {
|
||||
/** @type {import('./index').LilconfigResult} */
|
||||
const result = {
|
||||
config: null,
|
||||
filepath: '',
|
||||
};
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const visited = new Set();
|
||||
let dir = searchFrom;
|
||||
dirLoop: while (true) {
|
||||
if (cache) {
|
||||
const r = searchCache.get(dir);
|
||||
if (r !== undefined) {
|
||||
for (const p of visited) searchCache.set(p, r);
|
||||
return r;
|
||||
}
|
||||
visited.add(dir);
|
||||
}
|
||||
|
||||
for (const searchPlace of searchPlaces) {
|
||||
const filepath = path.join(dir, searchPlace);
|
||||
try {
|
||||
await fs.promises.access(filepath);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const content = String(await fsReadFileAsync(filepath));
|
||||
const loaderKey = path.extname(searchPlace) || 'noExt';
|
||||
const loader = loaders[loaderKey];
|
||||
|
||||
// handle package.json
|
||||
if (searchPlace === 'package.json') {
|
||||
const pkg = await loader(filepath, content);
|
||||
const maybeConfig = getPackageProp(packageProp, pkg);
|
||||
if (maybeConfig != null) {
|
||||
result.config = maybeConfig;
|
||||
result.filepath = filepath;
|
||||
break dirLoop;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle other type of configs
|
||||
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 dirLoop;
|
||||
}
|
||||
if (dir === stopDir || dir === parentDir(dir)) break dirLoop;
|
||||
dir = parentDir(dir);
|
||||
}
|
||||
|
||||
const transformed =
|
||||
// not found
|
||||
result.filepath === '' && result.config === null
|
||||
? transform(null)
|
||||
: transform(result);
|
||||
|
||||
if (cache) {
|
||||
for (const p of visited) searchCache.set(p, transformed);
|
||||
}
|
||||
|
||||
return transformed;
|
||||
},
|
||||
async load(filepath) {
|
||||
validateFilePath(filepath);
|
||||
const absPath = path.resolve(process.cwd(), filepath);
|
||||
if (cache && loadCache.has(absPath)) {
|
||||
return loadCache.get(absPath);
|
||||
}
|
||||
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 emplace(
|
||||
loadCache,
|
||||
absPath,
|
||||
transform({
|
||||
config: getPackageProp(packageProp, pkg),
|
||||
filepath: absPath,
|
||||
}),
|
||||
);
|
||||
}
|
||||
/** @type {import('./index').LilconfigResult} */
|
||||
const result = {
|
||||
config: null,
|
||||
filepath: absPath,
|
||||
};
|
||||
// handle other type of configs
|
||||
const isEmpty = content.trim() === '';
|
||||
if (isEmpty && ignoreEmptySearchPlaces)
|
||||
return emplace(
|
||||
loadCache,
|
||||
absPath,
|
||||
transform({
|
||||
config: undefined,
|
||||
filepath: absPath,
|
||||
isEmpty: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// cosmiconfig returns undefined for empty files
|
||||
result.config = isEmpty ? undefined : await loader(absPath, content);
|
||||
|
||||
return emplace(
|
||||
loadCache,
|
||||
absPath,
|
||||
transform(isEmpty ? {...result, isEmpty, config: undefined} : result),
|
||||
);
|
||||
},
|
||||
clearLoadCache() {
|
||||
if (cache) loadCache.clear();
|
||||
},
|
||||
clearSearchCache() {
|
||||
if (cache) searchCache.clear();
|
||||
},
|
||||
clearCaches() {
|
||||
if (cache) {
|
||||
loadCache.clear();
|
||||
searchCache.clear();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/** @type {import('./index').lilconfigSync} */
|
||||
module.exports.lilconfigSync = function lilconfigSync(name, options) {
|
||||
const {
|
||||
ignoreEmptySearchPlaces,
|
||||
loaders,
|
||||
packageProp,
|
||||
searchPlaces,
|
||||
stopDir,
|
||||
transform,
|
||||
cache,
|
||||
} = getOptions(name, options ?? {}, true);
|
||||
const searchCache = new Map();
|
||||
const loadCache = new Map();
|
||||
const emplace = makeEmplace(cache);
|
||||
|
||||
return {
|
||||
search(searchFrom = process.cwd()) {
|
||||
/** @type {import('./index').LilconfigResult} */
|
||||
const result = {
|
||||
config: null,
|
||||
filepath: '',
|
||||
};
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const visited = new Set();
|
||||
let dir = searchFrom;
|
||||
dirLoop: while (true) {
|
||||
if (cache) {
|
||||
const r = searchCache.get(dir);
|
||||
if (r !== undefined) {
|
||||
for (const p of visited) searchCache.set(p, r);
|
||||
return r;
|
||||
}
|
||||
visited.add(dir);
|
||||
}
|
||||
|
||||
for (const searchPlace of searchPlaces) {
|
||||
const filepath = path.join(dir, searchPlace);
|
||||
try {
|
||||
fs.accessSync(filepath);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const loaderKey = path.extname(searchPlace) || 'noExt';
|
||||
const loader = loaders[loaderKey];
|
||||
const content = String(fs.readFileSync(filepath));
|
||||
|
||||
// handle package.json
|
||||
if (searchPlace === 'package.json') {
|
||||
const pkg = loader(filepath, content);
|
||||
const maybeConfig = getPackageProp(packageProp, pkg);
|
||||
if (maybeConfig != null) {
|
||||
result.config = maybeConfig;
|
||||
result.filepath = filepath;
|
||||
break dirLoop;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle other type of configs
|
||||
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 dirLoop;
|
||||
}
|
||||
if (dir === stopDir || dir === parentDir(dir)) break dirLoop;
|
||||
dir = parentDir(dir);
|
||||
}
|
||||
|
||||
const transformed =
|
||||
// not found
|
||||
result.filepath === '' && result.config === null
|
||||
? transform(null)
|
||||
: transform(result);
|
||||
|
||||
if (cache) {
|
||||
for (const p of visited) searchCache.set(p, transformed);
|
||||
}
|
||||
|
||||
return transformed;
|
||||
},
|
||||
load(filepath) {
|
||||
validateFilePath(filepath);
|
||||
const absPath = path.resolve(process.cwd(), filepath);
|
||||
if (cache && loadCache.has(absPath)) {
|
||||
return loadCache.get(absPath);
|
||||
}
|
||||
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,
|
||||
};
|
||||
// handle other type of configs
|
||||
const isEmpty = content.trim() === '';
|
||||
if (isEmpty && ignoreEmptySearchPlaces)
|
||||
return emplace(
|
||||
loadCache,
|
||||
absPath,
|
||||
transform({
|
||||
filepath: absPath,
|
||||
config: undefined,
|
||||
isEmpty: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// cosmiconfig returns undefined for empty files
|
||||
result.config = isEmpty ? undefined : loader(absPath, content);
|
||||
|
||||
return emplace(
|
||||
loadCache,
|
||||
absPath,
|
||||
transform(isEmpty ? {...result, isEmpty, config: undefined} : result),
|
||||
);
|
||||
},
|
||||
clearLoadCache() {
|
||||
if (cache) loadCache.clear();
|
||||
},
|
||||
clearSearchCache() {
|
||||
if (cache) searchCache.clear();
|
||||
},
|
||||
clearCaches() {
|
||||
if (cache) {
|
||||
loadCache.clear();
|
||||
searchCache.clear();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user