init
This commit is contained in:
19
.cnb.yml
Normal file
19
.cnb.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# .cnb.yml
|
||||||
|
include:
|
||||||
|
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
|
||||||
|
|
||||||
|
.common_env: &common_env
|
||||||
|
env:
|
||||||
|
USERNAME: root
|
||||||
|
imports:
|
||||||
|
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
|
||||||
|
|
||||||
|
$:
|
||||||
|
vscode:
|
||||||
|
- docker:
|
||||||
|
image: docker.cnb.cool/kevisual/dev-env:latest
|
||||||
|
services:
|
||||||
|
- vscode
|
||||||
|
- docker
|
||||||
|
imports: !reference [.common_env, imports]
|
||||||
|
stages: !reference [.dev_template, stages]
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
storage
|
||||||
|
node_modules
|
||||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
|
||||||
|
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||||
25
bun.config.mjs
Normal file
25
bun.config.mjs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { resolvePath } from '@kevisual/use-config';
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
|
||||||
|
const entry = 'src/index.ts';
|
||||||
|
const naming = 'app';
|
||||||
|
const external = ['bullmq','ioredis'];
|
||||||
|
/**
|
||||||
|
* @type {import('bun').BuildConfig}
|
||||||
|
*/
|
||||||
|
await Bun.build({
|
||||||
|
target: 'node',
|
||||||
|
format: 'esm',
|
||||||
|
entrypoints: [resolvePath(entry, { meta: import.meta })],
|
||||||
|
outdir: resolvePath('./dist', { meta: import.meta }),
|
||||||
|
naming: {
|
||||||
|
entry: `${naming}.js`,
|
||||||
|
},
|
||||||
|
external: external,
|
||||||
|
env: 'KEVISUAL_*',
|
||||||
|
});
|
||||||
|
|
||||||
|
// const cmd = `dts -i src/index.ts -o app.d.ts`;
|
||||||
|
const cmd = `dts -i ${entry} -o ${naming}.d.ts`;
|
||||||
|
execSync(cmd, { stdio: 'inherit' });
|
||||||
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "@kevisual/context",
|
||||||
|
"version": "0.0.4",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun src/dev.ts --watch",
|
||||||
|
"build": "bun bun.config.mjs",
|
||||||
|
"publish": "ev npm publish npm -p"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.14.0",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/app.js",
|
||||||
|
"types": "./dist/app.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@kevisual/load": "^0.0.6",
|
||||||
|
"@kevisual/types": "^0.0.10",
|
||||||
|
"@kevisual/use-config": "^1.0.19",
|
||||||
|
"@types/node": "^24.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
40
readme.md
Normal file
40
readme.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# 环境变量 context
|
||||||
|
|
||||||
|
上下文 的node或则浏览器当中,如果global中不存在context,则赋值到global,如果存在,则获取对应的。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { useContextKey } from '@kevisual/context';
|
||||||
|
|
||||||
|
const v = useContextKey('a', () => '123'); // 如果是第一次初始化,返回123,否则返回之前初始化的值
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### types
|
||||||
|
```ts
|
||||||
|
type GlobalContext<T = SimpleObject> = {
|
||||||
|
name?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
} & T;
|
||||||
|
declare const useEnv: <T = SimpleObject>(initEnv?: GlobalContext<T>, initKey?: string, isOverwrite?: boolean) => Required<GlobalContext<T>>;
|
||||||
|
type InitResult<T> = T | Promise<T> | null;
|
||||||
|
type InitFn<T> = () => T | Promise<T>;
|
||||||
|
type Init<T> = T | InitFn<T> | null;
|
||||||
|
type AsyncResult<T, ASYNC extends boolean = false> = ASYNC extends true ? Promise<T> : T;
|
||||||
|
type SimpleObject = Record<string, any>;
|
||||||
|
declare const useEnvKey: <T = any>(key: string, init?: Init<T>, initKey?: string) => InitResult<T>;
|
||||||
|
declare const usePageEnv: (init?: Init<any>, initKey?: string) => any;
|
||||||
|
declare const useEnvKeyNew: (key: string, initKey?: string, opts?: {
|
||||||
|
getNew?: boolean;
|
||||||
|
init?: Init<any>;
|
||||||
|
}) => any;
|
||||||
|
declare const useContext: <T = SimpleObject>(initContext?: GlobalContext<T>, isOverwrite?: boolean) => Required<GlobalContext<T>>;
|
||||||
|
declare const useContextKey: <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean) => AsyncResult<T, ASYNC>;
|
||||||
|
declare const usePageContext: (init?: Init<any>) => any;
|
||||||
|
declare const useConfig: <T = SimpleObject>(initConfig?: Partial<GlobalContext<T>>, isOverwrite?: boolean) => Required<GlobalContext<Partial<GlobalContext<T>>>>;
|
||||||
|
declare const useConfigKey: <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean) => AsyncResult<T, ASYNC>;
|
||||||
|
declare const usePageConfig: (init?: Init<any>) => any;
|
||||||
|
|
||||||
|
export { useConfig, useConfigKey, useContext, useContextKey, useEnv, useEnvKey, useEnvKeyNew, usePageConfig, usePageContext, usePageEnv };
|
||||||
|
|
||||||
|
```
|
||||||
201
src/index.ts
Normal file
201
src/index.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import { isBrowser } from './utils/browser.ts';
|
||||||
|
import { getPathKey } from './utils/path-key.ts';
|
||||||
|
import { BaseLoad } from '@kevisual/load';
|
||||||
|
|
||||||
|
const gt = (globalThis as any) || window || self;
|
||||||
|
|
||||||
|
type GlobalContext<T = SimpleObject> = {
|
||||||
|
name?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
} & T;
|
||||||
|
// 从window对象中获取全局的环境变量,如果没有则初始化一个
|
||||||
|
export const useEnv = <T = SimpleObject>(initEnv?: GlobalContext<T>, initKey = 'config', isOverwrite?: boolean) => {
|
||||||
|
const env: GlobalContext<T> = gt[initKey];
|
||||||
|
const _env = env || initEnv;
|
||||||
|
// 环境不存在则初始化一个
|
||||||
|
if (!env) {
|
||||||
|
if (_env) {
|
||||||
|
gt[initKey] = _env;
|
||||||
|
} else {
|
||||||
|
gt[initKey] = {};
|
||||||
|
}
|
||||||
|
} else if (isOverwrite) {
|
||||||
|
// 环境存在且是覆盖模式
|
||||||
|
gt[initKey] = { ...env, ...initEnv };
|
||||||
|
}
|
||||||
|
|
||||||
|
return gt[initKey] as Required<GlobalContext<T>>;
|
||||||
|
};
|
||||||
|
type InitResult<T> = T | Promise<T> | null;
|
||||||
|
type InitFn<T> = () => T | Promise<T>;
|
||||||
|
type Init<T> = T | InitFn<T> | null;
|
||||||
|
type AsyncResult<T, ASYNC extends boolean = false> = ASYNC extends true ? Promise<T> : T;
|
||||||
|
type SimpleObject = Record<string, any>;
|
||||||
|
|
||||||
|
// 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象
|
||||||
|
export const useEnvKey = <T = any>(key: string, init?: Init<T>, initKey = 'config'): InitResult<T> => {
|
||||||
|
const _env = useEnv({}, initKey);
|
||||||
|
// 已经存在,直接返回
|
||||||
|
if (key && typeof _env[key] !== 'undefined') {
|
||||||
|
return _env[key];
|
||||||
|
}
|
||||||
|
// 不存在,但是有初始化函数,初始化的返回,同步函数,删除了重新加载?
|
||||||
|
if (key && init) {
|
||||||
|
if (typeof init !== 'function') {
|
||||||
|
_env[key] = init as T;
|
||||||
|
}
|
||||||
|
// 如果是函数,执行函数
|
||||||
|
if (typeof init === 'function') {
|
||||||
|
const result = (init as InitFn<T>)();
|
||||||
|
// 如果结果是Promise,处理异步结果
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return result.then((res) => {
|
||||||
|
_env[key] = res;
|
||||||
|
return res as T;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_env[key] = result;
|
||||||
|
}
|
||||||
|
return _env[key];
|
||||||
|
}
|
||||||
|
if (key) {
|
||||||
|
// 加载
|
||||||
|
const baseLoad = new BaseLoad();
|
||||||
|
const voidFn = async () => {
|
||||||
|
return _env[key];
|
||||||
|
};
|
||||||
|
const checkFn = async () => {
|
||||||
|
const loadRes = await baseLoad.load(voidFn, {
|
||||||
|
key,
|
||||||
|
isReRun: true,
|
||||||
|
checkSuccess: () => _env[key],
|
||||||
|
timeout: 5 * 60 * 1000,
|
||||||
|
interval: 1000,
|
||||||
|
//
|
||||||
|
});
|
||||||
|
if (loadRes.code !== 200) {
|
||||||
|
console.error('load key error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _env[key];
|
||||||
|
};
|
||||||
|
return checkFn();
|
||||||
|
}
|
||||||
|
// 不存在,没有初始化函数
|
||||||
|
console.error('key is empty ');
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePageEnv = (init?: Init<any>, initKey = 'config') => {
|
||||||
|
const { id } = getPathKey();
|
||||||
|
return useEnvKey(id, init, initKey);
|
||||||
|
};
|
||||||
|
export const useEnvKeyNew = (key: string, initKey = 'config', opts?: { getNew?: boolean; init?: Init<any> }) => {
|
||||||
|
const _env = useEnv({}, initKey);
|
||||||
|
if (key) {
|
||||||
|
delete _env[key];
|
||||||
|
}
|
||||||
|
if (opts?.getNew && opts.init) {
|
||||||
|
return useEnvKey(key, opts.init, initKey);
|
||||||
|
} else if (opts?.getNew) {
|
||||||
|
return useEnvKey(key, null, initKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useContext = <T = SimpleObject>(initContext?: GlobalContext<T>, isOverwrite?: boolean) => {
|
||||||
|
return useEnv(initContext, 'context', isOverwrite);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useContextKey = <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean): AsyncResult<T, ASYNC> => {
|
||||||
|
if (isNew) {
|
||||||
|
return useEnvKeyNew(key, 'context', { getNew: true, init }) as any;
|
||||||
|
}
|
||||||
|
return useEnvKey(key, init, 'context') as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const use = useContextKey;
|
||||||
|
|
||||||
|
export const usePageContext = (init?: Init<any>) => {
|
||||||
|
const { id } = getPathKey();
|
||||||
|
return useContextKey(id, init);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useConfig = <T = SimpleObject>(initConfig?: Partial<GlobalContext<T>>, isOverwrite?: boolean) => {
|
||||||
|
return useEnv(initConfig, 'config', isOverwrite);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useConfigKey = <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean): AsyncResult<T, ASYNC> => {
|
||||||
|
if (isNew) {
|
||||||
|
return useEnvKeyNew(key, 'config', { getNew: true, init });
|
||||||
|
}
|
||||||
|
return useEnvKey(key, init, 'config') as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePageConfig = (init?: Init<any>) => {
|
||||||
|
const { id } = getPathKey();
|
||||||
|
return useConfigKey(id, init);
|
||||||
|
};
|
||||||
|
|
||||||
|
class InitEnv {
|
||||||
|
static isInit = false;
|
||||||
|
|
||||||
|
static init(opts?: { load?: boolean; page?: boolean }) {
|
||||||
|
if (InitEnv.isInit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { load = true, page = false } = opts || {};
|
||||||
|
InitEnv.isInit = true;
|
||||||
|
// bind to window, 必须要的获取全局的环境变量
|
||||||
|
// @ts-ignore
|
||||||
|
gt.useConfigKey = useConfigKey;
|
||||||
|
// @ts-ignore
|
||||||
|
gt.useContextKey = useContextKey;
|
||||||
|
// @ts-ignore
|
||||||
|
gt.use = use;
|
||||||
|
// @ts-ignore
|
||||||
|
gt.webEnv = { useConfigKey, useContextKey, use };
|
||||||
|
// @ts-ignore
|
||||||
|
load && (gt.Load = BaseLoad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InitEnv.init();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从环境变量获取
|
||||||
|
* @param envKey
|
||||||
|
* @param initKey
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useKey = <T = string>(envKey: T, initKey = 'context') => {
|
||||||
|
let key: string = envKey as unknown as string;
|
||||||
|
if (!key) return null;
|
||||||
|
const _env = useEnv({}, initKey);
|
||||||
|
// 已经存在,直接返回
|
||||||
|
if (typeof _env[key] !== 'undefined') {
|
||||||
|
return _env[key];
|
||||||
|
}
|
||||||
|
if (!isBrowser) {
|
||||||
|
// 如果是node类,从process.env获取对应的环境变量
|
||||||
|
const nodeEev = gt?.process?.env;
|
||||||
|
if (typeof nodeEev !== 'undefined') {
|
||||||
|
const value = nodeEev[key];
|
||||||
|
if (typeof value !== 'undefined') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* 从浏览器的storage获取变量值
|
||||||
|
*/
|
||||||
|
const storage = gt?.localStorage;
|
||||||
|
if (typeof storage !== 'undefined') {
|
||||||
|
const value = storage.getItem(key);
|
||||||
|
if (typeof value !== 'undefined') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
33
src/test/a.ts
Normal file
33
src/test/a.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { useConfigKey, useConfig } from '@/index.ts';
|
||||||
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
const v = useConfigKey<{ name: string }>('a', async () => {
|
||||||
|
await sleep(1000);
|
||||||
|
return {
|
||||||
|
name: 'a',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('v', v.name);
|
||||||
|
|
||||||
|
const v2 = await useConfigKey<{ name: string }, true>('a');
|
||||||
|
|
||||||
|
console.log('v2', v2.name);
|
||||||
|
|
||||||
|
const v3 = useConfigKey<{ name: string }>('a', {
|
||||||
|
name: 'bbba',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('v3', v3.name);
|
||||||
|
|
||||||
|
const b = useConfig<{ f: string }>(
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
age: 18,
|
||||||
|
getName: () => {
|
||||||
|
return 'b';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('b', b.name, b.age, b.getName?.());
|
||||||
1
src/utils/browser.ts
Normal file
1
src/utils/browser.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||||
14
src/utils/path-key.ts
Normal file
14
src/utils/path-key.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export const getPathKey = () => {
|
||||||
|
if (!isBrowser) {
|
||||||
|
return { path: '', key: '', id: '', prefix: '' };
|
||||||
|
}
|
||||||
|
// 从localtion.href的路径中,/a/b 中 a为path,b为key
|
||||||
|
const pathname = location.pathname;
|
||||||
|
const paths = pathname.split('/');
|
||||||
|
let [path, key] = paths.slice(1);
|
||||||
|
path = path || '';
|
||||||
|
key = key || '';
|
||||||
|
return { path, key, id: path + '---' + key, prefix: `/${path}/${key}` };
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kevisual/types/json/backend.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules/@types",
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
],
|
||||||
|
"*": [
|
||||||
|
"types/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user