diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..6ca46a7 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm run lint diff --git a/package.json b/package.json index ef0179b..47ca2fc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "", "scripts": { "nimpl:config": "pnpm --filter @nimpl/config", - "eslint": "eslint \"package/\"" + "lint": "eslint \"package/\"", + "eslint": "eslint", + "prepare": "husky" }, "keywords": [], "author": "", @@ -13,6 +15,7 @@ "eslint": "8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "husky": "^9.0.11", "prettier": "^3.2.5", "typescript-eslint": "^7.6.0" } diff --git a/package/src/build-config.ts b/package/src/build-config.ts index c1d6009..1c26b75 100644 --- a/package/src/build-config.ts +++ b/package/src/build-config.ts @@ -1 +1 @@ -export const buildConfig = process.env.NIMPL_CONFIG_BUILD && JSON.parse(process.env.NIMPL_CONFIG_BUILD); \ No newline at end of file +export const buildConfig = process.env.NIMPL_CONFIG_BUILD && JSON.parse(process.env.NIMPL_CONFIG_BUILD); diff --git a/package/src/lib/configure-package-types.ts b/package/src/lib/configure-package-types.ts index 72a4cb0..12efa05 100644 --- a/package/src/lib/configure-package-types.ts +++ b/package/src/lib/configure-package-types.ts @@ -1,20 +1,22 @@ -import path from 'path'; -import { existsSync, writeFileSync } from 'fs'; +import path from "path"; +import { existsSync, writeFileSync } from "fs"; const configurePackageTypes = (folder: string) => { - const configDeclarationsPath = path.join(process.cwd(), 'nimpl-config.d.ts'); + const configDeclarationsPath = path.join(process.cwd(), "nimpl-config.d.ts"); const configFolderPath = path.join(process.cwd(), folder); if (!existsSync(configFolderPath)) { - throw new Error('@nimpl/config: config folder doesn\'t exist'); + throw new Error("@nimpl/config: config folder doesn't exist"); } const configFolderRelativePath = path.relative(process.cwd(), configFolderPath); - const normalizedRelativePath = './' + configFolderRelativePath.replace(/\/{2,}|\\+/g, '/').replace(/\/$/, ''); + const normalizedRelativePath = "./" + configFolderRelativePath.replace(/\/{2,}|\\+/g, "/").replace(/\/$/, ""); if (!existsSync(configDeclarationsPath)) { console.log(`nimpl-config: nimpl-config.d.ts file with configs types was generated`); - writeFileSync(configDeclarationsPath, `// NOTE: This file should not be edited + writeFileSync( + configDeclarationsPath, + `// NOTE: This file should not be edited // see https://nimpl.tech/config/configuration#typescript for more information. declare module "@nimpl/config/use-runtime-config" { @@ -33,8 +35,9 @@ declare module "@nimpl/config/postbuild-config" { declare module "@nimpl/config/server-config" { export declare const serverConfig: Promise; } -`); +`, + ); } -} +}; export default configurePackageTypes; diff --git a/package/src/lib/format-config.ts b/package/src/lib/format-config.ts index 1cda0d9..001ea0b 100644 --- a/package/src/lib/format-config.ts +++ b/package/src/lib/format-config.ts @@ -1,8 +1,8 @@ import type { Config, ConfigItem } from "./types"; const isMergeablePart = (part: ConfigItem): part is Record => { - return Boolean(part) && typeof part === 'object' && !Array.isArray(part); -} + return Boolean(part) && typeof part === "object" && !Array.isArray(part); +}; const mergeParts = async (basePart: ConfigItem, parts: ConfigItem[], subKey?: string): Promise => { if (isMergeablePart(basePart)) { @@ -15,45 +15,51 @@ const mergeParts = async (basePart: ConfigItem, parts: ConfigItem[], subKey?: st acc.push(cur[key]); } } else { - throw new Error(`Different type for "${subKey}". Should be: object; got: ${Array.isArray(cur) ? 'Array' : typeof cur}`); + throw new Error( + `Different type for "${subKey}". Should be: object; got: ${Array.isArray(cur) ? "Array" : typeof cur}`, + ); } return acc; }, []); - newPart[key] = await mergeParts(value, existedParts, `${subKey ? `${subKey}.` : ''}${key}`); + newPart[key] = await mergeParts(value, existedParts, `${subKey ? `${subKey}.` : ""}${key}`); } return newPart; } - if (typeof basePart === 'function') { + if (typeof basePart === "function") { const basePartResult = await basePart(); - const partsResult = await Promise.all(parts.map(part => { - if (typeof part === 'function') { - return part(); - } else { - throw new Error(`Different type for "${subKey}". Should be: function; got: ${typeof part}`); - } - })) + const partsResult = await Promise.all( + parts.map((part) => { + if (typeof part === "function") { + return part(); + } else { + throw new Error(`Different type for "${subKey}". Should be: function; got: ${typeof part}`); + } + }), + ); const newPart = await mergeParts(basePartResult, partsResult, subKey); return newPart; } - const lastExistedPart = parts.findLast(part => part !== undefined); + const lastExistedPart = parts.findLast((part) => part !== undefined); if (lastExistedPart) { if (Array.isArray(basePart) && !Array.isArray(lastExistedPart)) { throw new Error(`Different type for "${subKey}". Should be: Array; got: ${typeof lastExistedPart}`); } if (lastExistedPart !== null && typeof basePart !== typeof lastExistedPart) { - throw new Error(`Different type for "${subKey}". Should be: ${typeof basePart}; got: ${typeof lastExistedPart}`); + throw new Error( + `Different type for "${subKey}". Should be: ${typeof basePart}; got: ${typeof lastExistedPart}`, + ); } return lastExistedPart; } return lastExistedPart || basePart; -} +}; export const mergeConfigs = async (baseConfig: Config, configs: Config[]) => { const result = await mergeParts(baseConfig, configs); return result; -} +}; diff --git a/package/src/lib/get-build-config.ts b/package/src/lib/get-build-config.ts index 04d4d71..1890529 100644 --- a/package/src/lib/get-build-config.ts +++ b/package/src/lib/get-build-config.ts @@ -1,8 +1,8 @@ import getConfig from "./get-config"; const getBuildConfig = async (configFolder?: string) => { - const config = await getConfig({ variant: 'build', configFolder }); + const config = await getConfig({ variant: "build", configFolder }); return config; -} +}; export default getBuildConfig; diff --git a/package/src/lib/get-config.ts b/package/src/lib/get-config.ts index f19573c..0c41334 100644 --- a/package/src/lib/get-config.ts +++ b/package/src/lib/get-config.ts @@ -3,10 +3,10 @@ import { injectEnvsToConfig } from "./inject-envs"; import { findConfigs, getConfigVariantFolder, loadConfig } from "./utils"; type GetConfigParam = { - variant: 'build' | 'postbuild' | 'server' | 'runtime'; + variant: "build" | "postbuild" | "server" | "runtime"; env?: string; configFolder?: string; -} +}; const getConfig = async ({ variant, env, configFolder }: GetConfigParam) => { const variantFolder = getConfigVariantFolder(variant, configFolder); @@ -25,6 +25,6 @@ const getConfig = async ({ variant, env, configFolder }: GetConfigParam) => { const config = await mergeConfigs(defaultConfig, otherConfigs.filter(Boolean)); return config; -} +}; export default getConfig; diff --git a/package/src/lib/get-postbuild-config.ts b/package/src/lib/get-postbuild-config.ts index 6072efc..1ef315e 100644 --- a/package/src/lib/get-postbuild-config.ts +++ b/package/src/lib/get-postbuild-config.ts @@ -2,9 +2,9 @@ import getConfig from "./get-config"; const getPostbuildConfig = async (envs: string[], configFolder?: string) => { const configsEntries = await Promise.all( - envs.map((env) => getConfig({ variant: 'postbuild', env, configFolder }).then(config => [env, config])) - ) + envs.map((env) => getConfig({ variant: "postbuild", env, configFolder }).then((config) => [env, config])), + ); return Object.fromEntries(configsEntries); -} +}; export default getPostbuildConfig; diff --git a/package/src/lib/get-runtime-config.ts b/package/src/lib/get-runtime-config.ts index a93608b..9c26cf6 100644 --- a/package/src/lib/get-runtime-config.ts +++ b/package/src/lib/get-runtime-config.ts @@ -1,8 +1,8 @@ import getConfig from "./get-config"; const getRuntimeConfig = async (configFolder?: string) => { - const config = await getConfig({ variant: 'runtime', configFolder }); + const config = await getConfig({ variant: "runtime", configFolder }); return config; -} +}; export default getRuntimeConfig; diff --git a/package/src/lib/get-server-config.ts b/package/src/lib/get-server-config.ts index 758f072..84ffd44 100644 --- a/package/src/lib/get-server-config.ts +++ b/package/src/lib/get-server-config.ts @@ -1,8 +1,8 @@ import getConfig from "./get-config"; const getServerConfig = async (configFolder?: string) => { - const config = await getConfig({ variant: 'server', configFolder }); + const config = await getConfig({ variant: "server", configFolder }); return config; -} +}; export default getServerConfig; diff --git a/package/src/lib/inject-envs.ts b/package/src/lib/inject-envs.ts index 4599c96..f2c143e 100644 --- a/package/src/lib/inject-envs.ts +++ b/package/src/lib/inject-envs.ts @@ -1,7 +1,7 @@ import type { EnvConfig, EnvConfigItem } from "./types"; const injectEnvs = (part: EnvConfigItem): EnvConfigItem => { - if (typeof part === 'string') { + if (typeof part === "string") { return process.env[part]; } @@ -9,23 +9,21 @@ const injectEnvs = (part: EnvConfigItem): EnvConfigItem => { return part.map(injectEnvs); } - if (part && typeof part === 'object') { + if (part && typeof part === "object") { const newPart: EnvConfigItem = {}; - Object.entries(part).map(([key, value]) => ( - newPart[key] = injectEnvs(value) - )); + Object.entries(part).map(([key, value]) => (newPart[key] = injectEnvs(value))); return newPart; } - throw new Error(`Invalid value in envs config: ${part}`) -} + throw new Error(`Invalid value in envs config: ${part}`); +}; export const injectEnvsToConfig = (config: EnvConfig) => { const newConfig: EnvConfig = {}; Object.entries(config).map(([key, value]) => { newConfig[key] = injectEnvs(value); - }) + }); return newConfig; -} \ No newline at end of file +}; diff --git a/package/src/lib/types.ts b/package/src/lib/types.ts index 20fc068..c819e7b 100644 --- a/package/src/lib/types.ts +++ b/package/src/lib/types.ts @@ -1,17 +1,17 @@ type ConfigItemEl = string | boolean | number | null; -type ConfigItemFunc = () => (ConfigItem | Promise); +type ConfigItemFunc = () => ConfigItem | Promise; -export type ConfigItem = {[key: string]: ConfigItem} | ConfigItem[] | ConfigItemEl | ConfigItemFunc; +export type ConfigItem = { [key: string]: ConfigItem } | ConfigItem[] | ConfigItemEl | ConfigItemFunc; export type Config = { [key: string]: ConfigItem; -} +}; type EnvConfigItemEl = string | boolean | number | null | undefined; -export type EnvConfigItem = {[key: string]: EnvConfigItem} | EnvConfigItem[] | EnvConfigItemEl; +export type EnvConfigItem = { [key: string]: EnvConfigItem } | EnvConfigItem[] | EnvConfigItemEl; export type EnvConfig = { [key: string]: EnvConfigItem; -} \ No newline at end of file +}; diff --git a/package/src/lib/utils.ts b/package/src/lib/utils.ts index 766bc0e..5d99550 100644 --- a/package/src/lib/utils.ts +++ b/package/src/lib/utils.ts @@ -1,10 +1,10 @@ -import path from "path" +import path from "path"; import { pathToFileURL } from "url"; -import { readdir } from 'fs/promises'; +import { readdir } from "fs/promises"; -export const getConfigVariantFolder = (variant: string, configFolder: string = 'config') => { +export const getConfigVariantFolder = (variant: string, configFolder: string = "config") => { return path.join(process.cwd(), configFolder, variant); -} +}; export const findConfigs = async (variantFolder: string, env?: string) => { let configs: string[] = []; @@ -21,29 +21,29 @@ export const findConfigs = async (variantFolder: string, env?: string) => { if (configs.includes(`${base}.json`)) { return path.join(variantFolder, `${base}.json`); } - } + }; - const defaultConfig = getFilePath('default'); + const defaultConfig = getFilePath("default"); if (!defaultConfig) { - throw new Error('Should exist default config'); + throw new Error("Should exist default config"); } const validConfigs = { defaultBase: defaultConfig, - defaultLocal: getFilePath('default.local'), + defaultLocal: getFilePath("default.local"), processBase: env && getFilePath(env), processLocal: env && getFilePath(`${env}.local`), - envsBase: getFilePath('envs'), - envsLocal: getFilePath('envs.local'), - } + envsBase: getFilePath("envs"), + envsLocal: getFilePath("envs.local"), + }; return validConfigs; -} +}; -const dynamicImport = new Function('p', 'return import(p)'); +const dynamicImport = new Function("p", "return import(p)"); export const loadConfig = async (path: string) => { const config = await dynamicImport(pathToFileURL(path).toString()); return config.default || config; -} +}; diff --git a/package/src/postbuild-config.ts b/package/src/postbuild-config.ts index f276c38..eceda54 100644 --- a/package/src/postbuild-config.ts +++ b/package/src/postbuild-config.ts @@ -1,3 +1,5 @@ -const configs: {[key: string]: any} | undefined = process.env.NIMPL_CONFIG_POSTBUILD && JSON.parse(process.env.NIMPL_CONFIG_POSTBUILD); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const configs: { [key: string]: any } | undefined = + process.env.NIMPL_CONFIG_POSTBUILD && JSON.parse(process.env.NIMPL_CONFIG_POSTBUILD); -export const postbuildConfig = process.env.NODE_ENV && configs?.[process.env.NODE_ENV] || null; +export const postbuildConfig = (process.env.NODE_ENV && configs?.[process.env.NODE_ENV]) || null; diff --git a/package/src/runtime-config-api.ts b/package/src/runtime-config-api.ts index 7ae1033..a291413 100644 --- a/package/src/runtime-config-api.ts +++ b/package/src/runtime-config-api.ts @@ -1,8 +1,8 @@ -import getRuntimeConfig from './lib/get-runtime-config'; +import getRuntimeConfig from "./lib/get-runtime-config"; const runtimeConfig = getRuntimeConfig(process.env.NIMPL_CONFIG_FOLDER); export const runtimeConfigApi = async () => { const config = await runtimeConfig; return Response.json(config); -} \ No newline at end of file +}; diff --git a/package/src/runtime-config-provider.tsx b/package/src/runtime-config-provider.tsx index c514bdf..dacf1f5 100644 --- a/package/src/runtime-config-provider.tsx +++ b/package/src/runtime-config-provider.tsx @@ -1,7 +1,7 @@ -'use client'; +"use client"; -import React, { useRef } from "react" -import type { Config } from "./lib/types" +import React, { useRef } from "react"; +import type { Config } from "./lib/types"; import { RuntimeConfigContext } from "./lib/runtime-config-context"; const RuntimeConfigProvider: React.FC<{ children: React.ReactNode; apiPath: string }> = ({ children, apiPath }) => { @@ -12,16 +12,12 @@ const RuntimeConfigProvider: React.FC<{ children: React.ReactNode; apiPath: stri if (!isLoadingRef.current) { isLoadingRef.current = true; fetch(apiPath) - .then(resp => resp.json()) - .then(data => setConfig(data)) + .then((resp) => resp.json()) + .then((data) => setConfig(data)); } - }, []) + }, []); - return ( - - {children} - - ) -} + return {children}; +}; export default RuntimeConfigProvider; diff --git a/package/src/use-runtime-config.ts b/package/src/use-runtime-config.ts index 96bc9d2..a7e100d 100644 --- a/package/src/use-runtime-config.ts +++ b/package/src/use-runtime-config.ts @@ -6,6 +6,6 @@ const useRuntimeConfig = (): Config | null => { const configData = useContext(RuntimeConfigContext); return configData; -} +}; export default useRuntimeConfig; diff --git a/package/src/with-nimpl-config.ts b/package/src/with-nimpl-config.ts index c9f6e2b..d551eb8 100644 --- a/package/src/with-nimpl-config.ts +++ b/package/src/with-nimpl-config.ts @@ -6,15 +6,18 @@ type NimplConfigParam = { envs?: string[]; targetEnv?: string; folder?: string; -} +}; const nimplConfig = ({ envs = [], targetEnv, folder }: NimplConfigParam) => { if (targetEnv && !envs.includes(targetEnv)) { - console.log(`@nimpl/config: an unknown env was passed (${targetEnv}), the allowed ones were: [${envs.join(', ')}]`); + console.log( + `@nimpl/config: an unknown env was passed (${targetEnv}), the allowed ones were: [${envs.join(", ")}]`, + ); } - configurePackageTypes(folder || 'config'); + configurePackageTypes(folder || "config"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any return async (nextConfig: any) => { if (!nextConfig) nextConfig = {}; if (!nextConfig.env) nextConfig.env = {}; @@ -30,7 +33,7 @@ const nimplConfig = ({ envs = [], targetEnv, folder }: NimplConfigParam) => { nextConfig.env.NIMPL_CONFIG_ENV = targetEnv; } return nextConfig; - } -} + }; +}; export default nimplConfig; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7722734..2a2ff23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: eslint-plugin-prettier: specifier: ^5.1.3 version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5) + husky: + specifier: ^9.0.11 + version: 9.0.11 prettier: specifier: ^3.2.5 version: 3.2.5 @@ -805,6 +808,12 @@ packages: engines: {node: '>=8'} dev: true + /husky@9.0.11: + resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + engines: {node: '>=18'} + hasBin: true + dev: true + /ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'}