@hi18n/cli
Version:
Message internationalization meets immutability and type-safety - command line tool
191 lines • 6.54 kB
JavaScript
import path from "node:path";
import { cosmiconfig } from "cosmiconfig";
import { TSESLint, TSESTree, } from "@typescript-eslint/utils";
import resolve from "resolve";
import * as jsonMfConnector from "./json-mf-connector.js";
const explorer = cosmiconfig("hi18n");
const DEFAULT_EXTENSIONS = [
".js",
".cjs",
".mjs",
".ts",
".cts",
".mts",
".jsx",
".tsx",
];
const DEFAULT_EXTENSIONS_TO_REMOVE = [".js", ".cjs", ".mjs"];
const DEFAULT_PARSER_OPTIONS = {
ecmaVersion: "latest",
sourceType: "module",
};
const configKeys = [
"include",
"exclude",
"parser",
"parserOptions",
"extensions",
"extensionsToRemove",
"baseUrl",
"paths",
"connector",
"connectorOptions",
];
export async function loadConfig(cwd) {
const cosmiconfigResult = (await explorer.search(cwd)) ?? {
config: {},
filepath: path.join(cwd, ".hi18nrc.json"),
isEmpty: true,
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { config, filepath } = cosmiconfigResult;
if (!isObject(config)) {
throw new Error("config: not an object");
}
if (!optional(isArrayOf(isString))(config["include"])) {
throw new Error("config.include: not an array of strings");
}
if (!optional(isArrayOf(isString))(config["exclude"])) {
throw new Error("config.exclude: not an array of strings");
}
if (!optional(oneof(isString, isLooseParserModule))(config["parser"])) {
throw new Error("config.parser: not a string nor a parser object");
}
if (!optional(isObject)(config["parserOptions"])) {
throw new Error("config.parserOptions: not an object");
}
if (!optional(isArrayOf(isString))(config["extensions"])) {
throw new Error("config.extensions: not an array of strings");
}
if (!optional(isArrayOf(isString))(config["extensionsToRemove"])) {
throw new Error("config.extensionsToRemove: not an array of strings");
}
if (!optional(isString)(config["baseUrl"])) {
throw new Error("config.baseUrl: not a string");
}
if (!optional(isRecordOf(isArrayOf(isString)))(config["paths"])) {
throw new Error("config.paths: not a record of arrays of strings");
}
if (!optional(oneof(isString, isConnectorDependency))(config["connector"])) {
throw new Error("config.connector: not a string nor a parser object");
}
if (!optional(isObject)(config["connectorOptions"])) {
throw new Error("config.connectorOptions: not an object");
}
for (const key of Object.keys(config)) {
if (!configKeys.includes(key)) {
throw new Error(`Unrecognized config: ${key}`);
}
}
const include = config["include"];
const exclude = config["exclude"];
const parser = resolveParser(config["parser"], filepath);
const parserOptions = config["parserOptions"] ??
DEFAULT_PARSER_OPTIONS;
const extensions = expandExtensions(config["extensions"]);
const extensionsToRemove = expandExtensionsToRemove(config["extensionsToRemove"]);
const baseUrl = expandBaseUrl(config["baseUrl"], filepath);
const paths = config["paths"];
if (paths && !baseUrl) {
throw new Error("baseUrl must be specified");
}
const connector = resolveConnector(config["connector"], filepath);
const connectorOptions = config["connectorOptions"] ?? {};
return {
configPath: filepath,
include,
exclude,
parser,
parserOptions,
extensions,
extensionsToRemove,
baseUrl,
paths,
connector,
connectorOptions,
};
}
function resolveParser(parser, filepath) {
if (typeof parser === "string") {
const parserPath = resolve.sync(parser, {
basedir: path.dirname(filepath),
});
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-return
return require(parserPath);
}
else if (typeof parser === "object" && parser != null) {
return parser;
}
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-return
return require("@typescript-eslint/parser");
}
function resolveConnector(connector, filepath) {
if (connector === "@hi18n/cli/json-mf-connector") {
return jsonMfConnector;
}
else if (typeof connector === "string") {
const connectorPath = resolve.sync(connector, {
basedir: path.dirname(filepath),
});
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-return
return require(connectorPath);
}
else if (typeof connector === "object") {
return connector;
}
return undefined;
}
function expandExtensions(extensions) {
if (extensions === undefined)
return DEFAULT_EXTENSIONS;
return extensions.flatMap((ext) => ext === "..." ? DEFAULT_EXTENSIONS : [ext]);
}
function expandExtensionsToRemove(extensionsToRemove) {
if (extensionsToRemove === undefined)
return DEFAULT_EXTENSIONS_TO_REMOVE;
return extensionsToRemove.flatMap((ext) => ext === "..." ? DEFAULT_EXTENSIONS_TO_REMOVE : [ext]);
}
function expandBaseUrl(baseUrl, filepath) {
if (baseUrl === undefined)
return undefined;
return path.resolve(path.dirname(filepath), baseUrl);
}
function isObject(x) {
return typeof x === "object";
}
function isString(x) {
return typeof x === "string";
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
function isFunction(x) {
return typeof x === "function";
}
function isArrayOf(pred) {
return function isArrayOf(x) {
return Array.isArray(x) && x.every((value) => pred(value));
};
}
function isRecordOf(pred) {
return function isRecordOf(x) {
return isObject(x) && Object.values(x).every((value) => pred(value));
};
}
function optional(pred) {
return function optional(x) {
return x === undefined || pred(x);
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function oneof(...preds) {
return function oneof(x) {
return preds.some((pred) => pred(x));
};
}
function isLooseParserModule(x) {
const xx = x;
return (typeof xx.parseForESLint === "function" || typeof xx.parse === "function");
}
function isConnectorDependency(x) {
return isObject(x) && isFunction(x["connector"]);
}
//# sourceMappingURL=config.js.map